1. 程式人生 > >iOS開發-Block使用及迴圈引用的解決

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 控制器1ViewController2 控制器2,控制器1

跳轉到控制器2,然後在控制器2觸發事件回撥修改控制器1的背景顏色為紅色。

ViewController2的實現

#import <UIKit/UIKit.h>

@interfaceViewController2 : UIViewController

/**

 *  定義了一個changeColorBlock。這個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關鍵詞就可以

去禁止blockself進行強引用或者強制增加引用計數。

若產生警告,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了!