OC Block 其實功能就類似C語言的函數指針,js中的閉包之類的。把代碼塊當做一個變量就行操作,有自己的變量和作用域。
簡單看一下Block的語法和可能出現的問題:
Block語法:
block語法相對寬松,很多部分都可以省略,常規上我們實現一個block需要有以下幾個部分
^ 返回值類型 參數列表 表達式
例如
^int (int count){return count+1;};//返回值為int 參數為int 表達式為 count+1; ^void (void){NSLog(@"void");}; //返回值為void 參數void 表達式為 NSLog(@"void");
可以看到語法相對簡單,而且返回值類型可以省略那么以上兩個block就變
^(int count){return count+1;}; ^(void){NSLog(@"void");};
如果不適用參數,那么返回值列表也可以省略
^(int count){return count+1;}; ^{NSLog(@"void");};
Block類型變量
Blcok類型的變量可以接受對應的Block,上例中兩個Block就需要以下兩種Block類型變量接收
int (^intBlock)(int) = ^(int count){return count+1;}; void (^voidBlock)(void) = ^{NSLog(@"void");};
上例中,可以看到變量定義格式
返回值類型 (^變量名稱) (返回值類型)
如果嫌這種定義方式麻煩的話,可以使用typedef來簡化定義方式
typedef int (^typedefBlock) (int); typedefBlock block = ^(int count){return count+1;};
截獲變量值
Block中,可以使用調用Block之前的變量的值,例如
int a = 5; int (^intBlock)(int) = ^(int count){return count+a;}; NSLog(@"%d",intBlock(5)); 2015-08-02 17:25:53.393 Dispatch[9970:3096937] 10
可以看到輸出結果是10,說明intBlock截獲了變量a的值。
但是如果我們想要修改a的值,就會出現錯誤可以自己嘗試一下。
想要修改需要在變量前添加__block修飾符,說明該變量在block中是可以被修改的。
__block int a = 5; int (^intBlock)(int) = ^(int count){a = 3; return count+a;}; NSLog(@"%d",intBlock(5)); 2015-08-02 17:33:11.629 Dispatch[10118:3136172] 8
輸出結果為8,說明已經被我們修改了。
同樣的道理,對已OC對象來說也是,如果調用方法使用該變量可以,但是對變量進行賦值操作就需要加上__block修飾符。
Block循環引用
循環引用出現條件,該對象持有Block的成員屬性,同時在Block中使用self。這樣會造成Block和對象之間的相互引用,互相都無法釋放,形成內存泄露。
@interface ViewController (){ voidBlock _voidBlock;}- (void)viewDidLoad { [super viewDidLoad]; // __block int a = 5; _voidBlock = ^{NSLog(@"%@",self);}; _voidBlock();}這樣寫編譯去會提示我們self的強引用在Block中使用。
還有一種,如果我們在Block中使用了成員屬性,同樣會造成內存泄露。因為成員屬性是self指針指向的對象,還是在Block中持有了self。
__weak
為了避免這樣的情況發生,我們再上面的例子中稍微修改一下
- (void)viewDidLoad { [super viewDidLoad]; // __block int a = 5; __weak ViewController *temp = self; _voidBlock = ^{NSLog(@"%@",temp);}; _voidBlock();}
使用弱引用對象就能很好的避免這種情況。
再有就是用__block也能夠避免循環。
- (void)viewDidLoad { [super viewDidLoad]; // __block int a = 5; __block ViewController *temp = self; _voidBlock = ^{NSLog(@"%@",temp); temp = nil;}; _voidBlock();}
注意這樣寫必須調用該Block,執行Block代碼才行,如果不執行還是會造成內存泄露。
新聞熱點
疑難解答