Objective-C block深刻理解

1、block是什麼?閉包

block是帶有自動變量(局部變量)的匿名函數。它是C語言的擴展功能,C語言標準並不支持block。 app

block是Objective-C的閉包實現,正如C++中的Lambda表達式。閉包簡單理解即函數中的函數,閉包在JavaScript中是一個很重要的概念。做爲「函數中的函數」,block跟函數很相似。 函數

 

一、block變量 VS 函數指針變量spa

return_type (^block_name) (parameter list) // ^標識block,參數名可省略
int (^blk) (int, int) = ^(int a, int b) {
    return a+b;    
}; // 建立一個匿名block,並賦值給一個名爲blk的block變量

int ret = blk(10, 20); // block調用

 

return_type (*fptr_name) (parameter list) // 函數指針,參數名可省略
int f(int a, int b) {
    return a+b;    
} // 函數定義
int (*fptr)(int, int); // 函數指針聲明

fptr = f; // 函數名f其實就是個指針

int ret = fptr(10, 20); // 經過函數指針調用函數

一般的變量定義語句是像下面這樣的,可是block變量的語法形式比較難記:指針

變量類型 變量名 = 變量值;

爲此,能夠用typedef給block變量類型起個簡單的別名 code

typedef int (^blk) (int, int); // 相比正常的block變量聲明,只是前面多了typedef,blk就變成了這種block變量類型的別名。

blk sumBlk = ^(int a, int b) {
    return a+b;
}; // 這樣就回到熟悉的變量定義語句格式了  

 

二、block也是一種對象,根據它在內存中的位置,分爲全局block、棧block和堆block。能夠經過NSLog打印識別具體是哪種block對象。對象

1)沒引用外部變量的是全局blockblog

int a = 1;
void (^blk) () = ^{
    //NSLog(@"%d", a);
};
NSLog(@"%@", blk); // <__NSGlobalBlock__: 0x102d23be0>

2)引用了外部變量,在MRC下是棧block,能夠經過[block copy]把棧block拷貝到堆上,轉成堆block;在ARC下,不少時候系統幫咱們完成了拷貝,具體何時,要實測。ip

int a = 1;
void (^blk) () = ^{
    NSLog(@"%d", a);
};
NSLog(@"%@", blk); // ARC下 <__NSMallocBlock__: 0x60400005ba50>
// MRC下 <__NSStackBlock__: 0x7ffeefbff518>

3)棧block,相似局部變量,出了它的做用域,會被系統回收內存,再調用可能致使程序崩潰。 內存

4)堆block,正如其餘Objective-C對象那樣,利用引用計數進行內存管理。 

 

三、在block裏面,默認不能修改外部變量。這裏指的是不能修改變量自己,若是是指針類型,它指向的內容是能夠修改的。

 

局部變量

全局變量

靜態變量

__block變量

成員變量

屬性

 

- (void)testBlock {
    int i = 10;
    NSLog(@"[%p] [%d]", &i, i);
    void (^blk)() = ^(){
        NSLog(@"[%p] [%d]", &i, i);
        i++; // 不能修改,報錯:Variable is not assignable (missing __block type specifier)
    };
    blk();
}
 
輸出:
[0x60800044e810] [10]
[0x60800044e810] [10]

 

- (void)testBlock {
    NSMutableString *str = [[NSMutableString alloc] initWithString:@"abc"];
    NSLog(@"[%p] [%@]", str, str);
    void (^blk)() = ^(){
		NSLog(@"[%p] [%@]", str, str);
		[str appendString:@"def"]; // 能夠修改指針指向的內容
		str = [[NSMutableString alloc] initWithString:@"another str"]; // 不能修改指針自己,報錯:Variable is not assignable (missing __block type specifier)
    };
    blk();
    NSLog(@"[%p] [%@]", str, str);
}

輸出:
[0x60000025df10] [abc]
[0x60000025df10] [abc]
[0x60000025df10] [abcdef]

  

 

  

  

struct Block_descriptor {
    unsigned long int reserved;
    unsigned long int size;
    void (*copy)(void *dst, void *src);
    void (*dispose)(void *);
};

struct Block_layout {
    void *isa;
    int flags;
    int reserved; 
    void (*invoke)(void *, ...);
    struct Block_descriptor *descriptor;
    /* Imported variables. */
};

 

五、循環引用

1)緣由:對象A有block屬性,即持有block;而block中又用了對象A,使得block也持有了對象A。

self.blk = ^{
    [self method];
};

2)

__weak typeof(self) weakSelf = self;
self.blk = ^{
    __strong typeof(weakSelf) strongSelf = weakSelf;
    [self method];
};

 

block不會捕獲形參到內部持有

 

block也是對象

 

自動型,託管型,變量綁定

 

========================================================

 

循環引用

 

成員變量

 

持有對象的成員變量即間接持有對象。

 

__block修飾符能夠用來避免循環引用?block不會持有__block對象。

 

__block修飾符在MRC和ARC下區別很大?

相關文章
相關標籤/搜索