Block的認識和使用,Block認識使用
Blocks是Objective-C的匿名函數。block是iOS在4.0之後新增的程式文法。
Blocks有三種類型,_NSConcreteGlobalBlock,_NSConcreteStackBlock和_NSConcreteMallocBlock。
1._NSConcreteGlobalBlock 全部的靜態block,不會訪問任何外部變數。
2._NSConcreteStackBlock 儲存在棧中的block,當函數返回時會被銷毀。
3._NSConcreteMallocBlock 儲存在堆中的block,當引用計數為0時會被銷毀。
變數的複製
對於block外的變數引用,block預設是將其複製到其資料結構中來實現訪問的,如所示(圖片來自這裡):
#define TLog(prefix,Obj) {NSLog(@"變數記憶體位址:%p,變數值:%p,指向對象值:%@, --> %@",&Obj,Obj,Obj,prefix);}
上面為定義的宏變數
下面為代碼:
NSString*a =@"100";
TLog(@"a-->non block", a);
void(^block2)(void) = ^{
TLog(@"a-->block", a);
};
a = @"102";
block2();
a = nil;
block2();
列印結果為:
變數記憶體位址:0x7fff570d3b28,變數值:0x108b2c190,指向對象值:100,--> a-->non block
變數記憶體位址:0x7ff8bb533d00,變數值:0x108b2c190,指向對象值:100,--> a-->block
變數記憶體位址:0x7ff8bb533d00,變數值:0x108b2c190,指向對象值:100,--> a-->block
說明外部變數被copy到block中,外面變數的變化,不會引起block內的變數變化。兩個不是同一個變數了。同時外部變數不可以在block中做修改。
__weak修改代碼如下:
__weak NSString *b =@"123";
TLog(@"b-->non block", b);
void(^blocka)(void) = ^{
TLog(@"c-->block", b);
};
blocka();
b = nil;
TLog(@"b-->1", b);
blocka();
列印結果如下:
變數記憶體位址:0x7fff57ddcb28,變數值:0x107e23190,指向對象值:123,--> b-->non block
變數記憶體位址:0x7fb95ae2dd10,變數值:0x107e23190,指向對象值:123,--> c-->block
變數記憶體位址:0x7fff57ddcb28,變數值:0x0,指向對象值:(null),--> b-->1
變數記憶體位址:0x7fb95ae2dd10,變數值:0x107e23190,指向對象值:123,--> c-->block
__weak修飾,整個內容copy,所以b = nil,不會影響到block內部。
對於用__block修飾的外部變數引用,block是複製其引用地址來實現訪問的,如所示(圖片來自這裡):
__blockNSString*a =@"100";
TLog(@"a-->non block", a);
void(^block2)(void) = ^{
TLog(@"a-->block", a);
};
a = @"102";
block2();
a = nil;
block2();
列印結果為:
變數記憶體位址:0x7fff54e04b28,變數值:0x10adfb1a0,指向對象值:100,--> a-->non block
變數記憶體位址:0x7ff52a604078,變數值:0x10adfb220,指向對象值:102,--> a-->block
變數記憶體位址:0x7ff52a604078,變數值:0x0,指向對象值:(null),--> a-->block
用__block修飾的變數,只是進行指標的copy,所以當外部變數修改時也影響了block內部的變化。和用static修飾的效果一樣。可以在block內部對外部變數進行修改。
怎麼避免block的循環參考
NSString *a = [[NSStringalloc]initWithFormat:@"%d",123];
TLog(@"a", a);
__weakNSString *b = a;//b只是進行了a的指標copy,所以a的值變化也會影響到b。同時a釋放時,b也釋放了。
TLog(@"b-->non block", b);
void(^blocka)(void) = ^{
TLog(@"b-->block", b);
};
blocka();
a = nil;
blocka();
列印結果如下:
變數記憶體位址:0x7fff53db5b28,變數值:0x7fd4b8f1a210,指向對象值:123,--> a
變數記憶體位址:0x7fff53db5b20,變數值:0x7fd4b8f1a210,指向對象值:123,--> b-->non block
變數記憶體位址:0x7fd4b8f17b50,變數值:0x7fd4b8f1a210,指向對象值:123,--> b-->block
變數記憶體位址:0x7fd4b8f17b50,變數值:0x0,指向對象值:(null),--> b-->block
從上面的結果可以看到
· block 內的b 和外部的 b 並不是同一個變數(內容copy)
· block 捕獲了b 同時也是對 a 進行了弱引用,當我在 block 外把 a 釋放了之後,block 內也讀不到這個變數了
· 當a 賦值 nil 時,block 內部的 b 也為 nil 了,也就是說 a實際上是被釋放了,可見 __weak 是可以避免循環參考問題的
· __weak 本身是可以避免循環參考的問題的,但是其會導致外部對象釋放了之後,block內部也訪問不到這個對象的問題,我們可以通過在 block 內部聲明一個 __strong 的變數來指向 weakObj,使外部對象既能在block 內部保持住,又能避免循環參考的問題
· 而使用__block
MyObject*obj = [MyObjectnew];
obj.text=@"123456";
TLog(@"obj", obj);
__blockMyObject*weakObj= obj;
TLog(@"weakObj-->non block", weakObj);
void(^block2)(void) = ^{
TLog(@"weakObj-->block",weakObj);
};
block2();
obj = nil;
· block2();
· 列印結果如下:
變數記憶體位址:0x7fff5d723b28,變數值:0x7ff999c990e0,指向對象值:<MyObject:0x7ff999c990e0>, --> obj
變數記憶體位址:0x7fff5d723b20,變數值:0x7ff999c990e0,指向對象值:<MyObject:0x7ff999c990e0>, --> weakObj-->non block
變數記憶體位址:0x7ff999e3f098,變數值:0x7ff999c990e0,指向對象值:<MyObject:0x7ff999c990e0>, --> weakObj-->block
變數記憶體位址:0x7ff999e3f098,變數值:0x7ff999c990e0,指向對象值:<MyObject:0x7ff999c990e0>, --> weakObj-->block
當外部 obj 指向 nil 的時候,obj 理應被釋放,但實際上 blockObj 依然強引用著 obj,obj 其實並沒有被真正釋放。因此使用 __block並不能避免循環參考的問題。
__block 本身無法避免循環參考的問題,但是我們可以通過在 block 內部手動把 blockObj 賦值為 nil 的方式來避免循環參考的問題。另外一點就是 __block 修飾的變數在block 內外都是唯一的,要注意這個特性可能帶來的隱患。
參考連結http://blog.devtang.com/blog/2013/07/28/a-look-inside-blocks/http://honglu.me/2015/01/06/weak%E4%B8%8Eblock%E5%8C%BA%E5%88%AB/
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。