Objective-C—— Block,objective-cblock
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代碼才行,如果不執行還是會造成記憶體泄露。