iOS開發-Block使用及迴圈引用的解決
Block是一種比較特殊的資料型別。它可以儲存一段程式碼,在合適的時候取出來呼叫。
◦ 我們可以把Block當做Objective-C的匿名函式。Block允許開發者在兩個物件之間將任意的語句當做資料進行傳遞,往往這要比引用定義在別處的函式直觀。另外,block的實現具有封閉性(closure),而又能夠很容易獲取上下文的相關狀態資訊。
◦ block是程式碼塊,其本質和變數類似。不同的是程式碼塊儲存的資料是一個函式體。使用Block,就可以像其他標準函式一樣,傳入引數,並得到返回值。
block的格式:
a:Block的返回值型別,可以為空(void);
b:Block物件名稱,可以理解為變數名;
^:塊的語法標記,宣告b為一個Block物件;
c:第一個引數型別
d:第二個引數型別
name1,name2:引數名;
{}:Block程式碼塊的主題部分。
Block使用場景,可以在兩個介面的傳值,也可以對程式碼封裝作為引數的傳遞等。用過GCD就知道Block的精妙之處。
Block的修飾
ARC情況下
1.如果用copy修飾Block,該Block就會儲存在堆空間。則會對Block的內部物件進行強引用,導致迴圈引用。記憶體無法釋放。
解決方法:
新建一個指標(__weaktypeof(Target) weakTarget = Target )指向Block程式碼塊裡的物件,然後用weakTarget
2.如果用weak修飾Block,該Block就會存放在棧空間。不會出現迴圈引用問題。
MRC情況下
用copy修飾後,如果要在Block內部使用物件,則需要進行(__block typeof(Target) blockTarget =Target )處理。在Block裡面用blockTarget進行操作。
Block結合typedef使用
自己定義一個Block型別,用定義的型別去建立Block,更加簡單便捷。
這裡舉例一個Block回撥修改上一下介面的背景顏色。ViewController1 控制器1,ViewController2 控制器2,控制器1
ViewController2的實現
#import <UIKit/UIKit.h>
@interfaceViewController2 : UIViewController
/**
* 定義了一個changeColor的Block。這個changeColor必須帶一個引數,這個引數的型別必須為id型別的
* 無返回值
* @param id
*/
typedefvoid(^changeColor)(id);
/**
* 用上面定義的changeColor宣告一個Block,宣告的這個Block必須遵守宣告的要求。
*/
@property (nonatomic, copy) changeColorbackgroundColor;
@end
-(void)touchesBegan:(NSSet<UITouch *> *)toucheswithEvent:(UIEvent *)event{
//宣告一個顏色
UIColor *color = [UIColor redColor];
//用剛剛宣告的那個Block去回撥修改上一介面的背景色
self.backgroundColor(color);
}
ViewController1的實現
-(void)touchesBegan:(NSSet<UITouch *> *)toucheswithEvent:(UIEvent *)event{
ViewController2*vc =[[ViewController2 alloc]init];
// 回撥修改顏色
vc.backgroundColor = ^(UIColor *color){
self.view.backgroundColor = color;
};
[self.navigationController pushViewController:vcanimated:YES];
}
__block關鍵字的使用
◦ 在Block的程式碼塊裡,是不能修改在外面定義的變數,並且在給block賦值的時候,已經對程式碼塊裡的變數做了值的拷貝(只讀不可修改)。
int x = 5;
int (^block4)(int) =^(int y) {
int z =x + y;
returnz;
};
NSLog(@"%d,%d",x+=5,block4(5)); 列印的值是10,10;
分析:變數x在Block外定義的,在Block程式碼塊編譯的時候,取的x的值為之前的5(不可修改)。因此即使執行x += 5的使x的值變為10,但Block程式碼塊裡的x依然是5,所以block(5)的值為5+5=10。
◦ 在變數前新增__block關鍵字進行修飾後,此變數在Block程式碼塊裡的就是可更改的(可讀可寫),執行程式碼時取變數最新的值。
__block int x = 5;
int (^block4)(int) =^(int y) {
int z =x + y;
returnz;
};
NSLog(@"%d,%d",x+=5,block4(5)); 列印的值是10,15;
當一個物件中的block塊中的訪問自己的屬性會不會造成迴圈引用?
Block內部使用外部的一個物件,如果外部物件是強引用那麼內部會自動生成一個強引用,引用著外部物件。如果外部物件是弱引用那麼內部會自動生成一個弱引用,引用著外部物件。
在ARC下,只加一個__weak關鍵詞就可以
在非ARC下,只加一個__block關鍵詞就可以
去禁止block對self進行強引用或者強制增加引用計數。
若產生警告,weak變數被release掉,block程式碼塊未執行,則
1)ARC環境下:ARC環境下可以通過使用_weak宣告一個代替self的新變數代替原先的self,我們可以命名為weakSelf。通過這種方式告訴block,不要在block內部對self進行強制strong引用:(如果要相容ios4.3,則用__unsafe_unretained代替__weak,不過目前基本不需考慮這麼low的版本)
1 self.arr= @[@111, @222, @333];
2__weak typeof(self) weakSelf=self;
3 self.block= ^(NSString *name){
4 NSLog(@"arr:%@", weakSelf.arr); };
2)MRC環境下:解決方式與上述基本一致,只不過將__weak關鍵字換成__block即可,這樣的意思是告訴block:小子,不要在內部對self進行retain了!