Objective-C Block Part1-介紹&使用

什么是 Block ?

Block 是蘋果在 iOS4 添加的特性。它是一個帶自動變量(局部變量)的匿名函數,同時也是 OC 對象類型,所以可以把 Block 賦值給一個變量,也可以存儲在 NSArray NSDictionary 這樣的容器中,或者作為函數返回值。Block 等同于其他語言中的 closure lambda。 Block 使用簡單方便,在很多場景下可以替代 delegate。Block 在系統提供的 API 中也是隨處可見。

Block 的語法

下面是一個完整的 Block 定義規則,Block 標志性的標識是 ^ (caret 脫字符號),這是每個 Block 必須擁有的。剩下的和匿名函數相同。

^ 返回值類型 (參數列表) {表達式};
^ int(int v1, int v2) {return v1 + v2;};

如果返回值類型為 void , 沒有參數,這些都是可以省略,下面最簡模式的 Block:

^{表達式};
^{printf("hello world!");};

Block 也是 OC 對象類型 可以把 Block 賦值給變量或類屬性。也可以通過 typedef 去簡化定義 Block 類型。

typedef void(^blk_t)(); // 用 typedef 定義 Block 類型

void(^block1)() = ^{printf("簡化前");};
blk_t block2 = ^{printf("簡化后");};

Block 的使用規則

捕獲變量

Block 一個很大的優點就是可以捕獲外部變量在 Block 內使用,并且除了特定情況,只要 Block 存在這個被捕獲的變量就能夠一直使用。 這個規則對 局部變量 靜態變量 全局變量 靜態全局變量 都有效。但是其中的 局部變量 不能夠在 Block 中被重新賦值。可以對 局部變量 加上 __block 說明符去解決這個問題。 下面舉一個栗子來佐證剛才的說法,以下代碼基于 ARC :

typedef void(^blk_t)();

static int static_global_val = 1;                       // 靜態全局變量(C  基礎類型
static id static_global_obj;                            // 靜態全局變量(OC 對象類型
int global_val = 1;                                     // 全局變量(C  基礎類型
id  global_obj;                                         // 全局變量(OC 對象類型

@interface TObject : NSObject
@property (nonatomic) blk_t block;
@end

@implementation TObject
- (instancetype)init {
    self = [super init];
  
    int automatic_val = 1;                             // 自動變量(C  基礎類型
    id  automatic_obj = [[NSObject alloc] init];       // 自動變量(OC 對象類型
    static int static_val = 1;                         // 靜態變量(C  基礎類型
    static id  static_obj;                             // 靜態變量(OC 對象類型
    
    static_global_obj = [NSObject new];
    global_obj = [NSObject new];
    static_obj = [NSObject new];
    
    self.block = ^{
        NSLog(@"static_global_val: %d", static_global_val);
        NSLog(@"static_global_obj: %@", static_global_obj);
        NSLog(@"global_val: %d", global_val);
        NSLog(@"global_obj: %@", global_obj);
        NSLog(@"static_val: %d", static_val);
        NSLog(@"static_obj: %@", static_obj);
        NSLog(@"automatic_val: %d", automatic_val);
        NSLog(@"automatic_obj: %@", automatic_obj);
        
        static_global_val = 0;
        static_global_obj = [NSArray array];
        global_val = 0;
        global_obj = [NSArray array];
        static_val = 0;
        static_obj = [NSArray array];
      
//        automatic_val = 0;
//        automatic_obj = [NSArray array];
    };
    return self;
}
@end

int main(int argc, const char * argv[]) {
  
    TObject *obj = [[TObject alloc] init];
    obj.block();
    return 0;
}

上面的例子定義了各種各樣的變量并在 block 中使用它們,通過觀察他們的表現來佐證我們的觀點。在 Block 中注釋的兩行代碼試圖去更改 C 對象類型OC 對象類型 的自動變量,但是并沒有成功。這里編譯器均會報錯: Variable is not assignable (missing __block type specifier) ,編譯器告訴我們這兩個變量不能被賦值,可以通過加上 __block 說明符去解決這個問題。這剛好驗證了上面的說法。 下面的代碼塊里的內容是上面代碼執行后的輸出。雖然在 main 函數中執行 block 時,自動變量 automatic_val automatic_obj 已經超出了其所在的函數作用域,但是仍然能打印出里面的值。這點也是符合預期的。

2017-03-26 20:15:34.264803 BlockDemo static_global_val: 1
2017-03-26 20:15:34.265551 BlockDemo static_global_obj: <NSObject: 0x100202e00>
2017-03-26 20:15:34.265579 BlockDemo global_val: 1
2017-03-26 20:15:34.265724 BlockDemo global_obj: <NSObject: 0x1002000c0>
2017-03-26 20:15:34.265773 BlockDemo static_val: 1
2017-03-26 20:15:34.265825 BlockDemo static_obj: <NSObject: 0x100203b00>
2017-03-26 20:15:34.265860 BlockDemo automatic_val: 1
2017-03-26 20:15:34.265927 BlockDemo automatic_obj: <NSObject: 0x1002024b0>

正確的儲存 Block

文章的開頭我們就講到了 Block 是一個 OC 對象, 可以把它賦值給一個變量存儲起來。但是這里 Block 和普通OC對象還是有一點細小的區別的,操作不當有可能 Block 就會被提前釋放掉。

MRC 下要儲存定義在函數內并且截獲了自動變量的 Block 時。 如果期望它能超出函數作用域之外,需要先對 Block 進行 copy 操作,然后把返回的結果賦值給變量。或者賦值給 Property 時需要把它的 attribute 設置為 copy ,例如: @property (copy) blk_t block; 。 此時就和管理普通對象的內存無異了。可以對其 retain release

ARC 下則完全和普通對象一樣,使用 __strong 的修飾符的變量就好。不需要像 MRC 下去做 copy 操作

避免循環引用

上面已經展示過 Block 可以捕獲自動變量,并且可以讓其超過它自身所在的函數作用域而存在。Block 能有這個功能只是因為它持有了這個變量,這個變量只要 Block 存在它就會存在。但是這樣會有一個安全隱患—會產生循環引用。例如下面的例子:

/// 運行在 ARC 下:
typedef void(^blk_t)();

@interface TObject : NSObject
@property (nonatomic, copy) blk_t block;
@end

@implementation TObject
- (instancetype)init {
    self = [super init];
    self.block = ^{ NSLog(@"%@", self); };
    return self;
}
@end

上面你的代碼,self 持有 block,但 block 也持有了 self。所以就循環引用了,誰也釋放不了誰,造成內存泄漏。解決辦法如下:

- (instancetype)init {
    self = [super init];
    
    __block __typeof(self) weakSelf = self; // MRC 的情況下
    __weak __typeof(self) weakSelf = self;  // ARC 的情況下
    self.block = ^{ NSLog(@"%@", weakSelf);};
    return self;
}

MRC 下使用 __block 說明符去避免循環引用

ARC 下使用 __weak 修飾符去避免循環應用

這兩種方法都能在對應的內存管理機制下,讓 Block 不 retain 或 強持有 截獲的 self。 因為 self 持有 block。 所以也不用擔心 block 執行時 self 會被釋放。這就解決 Block 循環引用的問題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,983評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,772評論 3 422
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,947評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,201評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,960評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,350評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,406評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,549評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,104評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,914評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,089評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,647評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,340評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,753評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,007評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,834評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,106評論 2 375

推薦閱讀更多精彩內容