1、block是什麼?閉包
block是帶有自動變量(局部變量)的匿名函數。它是C語言的擴展功能,C語言標準並不支持block。 app
block是Objective-C的閉包實現,正如C++中的Lambda表達式。閉包簡單理解即函數中的函數,閉包在JavaScript中是一個很重要的概念。做爲「函數中的函數」,block跟函數很相似。 函數
一、block變量 VS 函數指針變量spa
return_type (^block_name) (parameter list) // ^標識block,參數名可省略
int (^blk) (int, int) = ^(int a, int b) { return a+b; }; // 建立一個匿名block,並賦值給一個名爲blk的block變量 int ret = blk(10, 20); // block調用
return_type (*fptr_name) (parameter list) // 函數指針,參數名可省略
int f(int a, int b) { return a+b; } // 函數定義 int (*fptr)(int, int); // 函數指針聲明 fptr = f; // 函數名f其實就是個指針 int ret = fptr(10, 20); // 經過函數指針調用函數
一般的變量定義語句是像下面這樣的,可是block變量的語法形式比較難記:指針
變量類型 變量名 = 變量值;
爲此,能夠用typedef給block變量類型起個簡單的別名 code
typedef int (^blk) (int, int); // 相比正常的block變量聲明,只是前面多了typedef,blk就變成了這種block變量類型的別名。 blk sumBlk = ^(int a, int b) { return a+b; }; // 這樣就回到熟悉的變量定義語句格式了
二、block也是一種對象,根據它在內存中的位置,分爲全局block、棧block和堆block。能夠經過NSLog打印識別具體是哪種block對象。對象
1)沒引用外部變量的是全局blockblog
int a = 1; void (^blk) () = ^{ //NSLog(@"%d", a); }; NSLog(@"%@", blk); // <__NSGlobalBlock__: 0x102d23be0>
2)引用了外部變量,在MRC下是棧block,能夠經過[block copy]把棧block拷貝到堆上,轉成堆block;在ARC下,不少時候系統幫咱們完成了拷貝,具體何時,要實測。ip
int a = 1; void (^blk) () = ^{ NSLog(@"%d", a); }; NSLog(@"%@", blk); // ARC下 <__NSMallocBlock__: 0x60400005ba50> // MRC下 <__NSStackBlock__: 0x7ffeefbff518>
3)棧block,相似局部變量,出了它的做用域,會被系統回收內存,再調用可能致使程序崩潰。 內存
4)堆block,正如其餘Objective-C對象那樣,利用引用計數進行內存管理。
三、在block裏面,默認不能修改外部變量。這裏指的是不能修改變量自己,若是是指針類型,它指向的內容是能夠修改的。
局部變量
全局變量
靜態變量
__block變量
成員變量
屬性
- (void)testBlock { int i = 10; NSLog(@"[%p] [%d]", &i, i); void (^blk)() = ^(){ NSLog(@"[%p] [%d]", &i, i); i++; // 不能修改,報錯:Variable is not assignable (missing __block type specifier) }; blk(); } 輸出: [0x60800044e810] [10] [0x60800044e810] [10]
- (void)testBlock { NSMutableString *str = [[NSMutableString alloc] initWithString:@"abc"]; NSLog(@"[%p] [%@]", str, str); void (^blk)() = ^(){ NSLog(@"[%p] [%@]", str, str); [str appendString:@"def"]; // 能夠修改指針指向的內容 str = [[NSMutableString alloc] initWithString:@"another str"]; // 不能修改指針自己,報錯:Variable is not assignable (missing __block type specifier) }; blk(); NSLog(@"[%p] [%@]", str, str); } 輸出: [0x60000025df10] [abc] [0x60000025df10] [abc] [0x60000025df10] [abcdef]
struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); }; struct Block_layout { void *isa; int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; /* Imported variables. */ };
五、循環引用
1)緣由:對象A有block屬性,即持有block;而block中又用了對象A,使得block也持有了對象A。
self.blk = ^{ [self method]; };
2)
__weak typeof(self) weakSelf = self; self.blk = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; [self method]; };
block不會捕獲形參到內部持有
block也是對象
自動型,託管型,變量綁定
========================================================
循環引用
成員變量
持有對象的成員變量即間接持有對象。
__block修飾符能夠用來避免循環引用?block不會持有__block對象。
__block修飾符在MRC和ARC下區別很大?