Objective-C block深入理解

一、block是什么?

block是带有自动变量(局部变量)的匿名函数。它是C语言的扩展功能,C语言标准并不支持block。 

block是Objective-C的闭包实现,正如C++中的Lambda表达式。闭包简单理解即函数中的函数,闭包在JavaScript中是一个很重要的概念。作为“函数中的函数”,block跟函数很类似。 

 

1、block变量 VS 函数指针变量

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变量类型起个简单的别名 

typedef int (^blk) (int, int); // 相比正常的block变量声明,只是前面多了typedef,blk就变成了这种block变量类型的别名。

blk sumBlk = ^(int a, int b) {
    return a+b;
}; // 这样就回到熟悉的变量定义语句格式了  

 

2、block也是一种对象,根据它在内存中的位置,分为全局block、栈block和堆block。可以通过NSLog打印识别具体是哪一种block对象。

1)没引用外部变量的是全局block

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

2)引用了外部变量,在MRC下是栈block,可以通过[block copy]把栈block拷贝到堆上,转成堆block;在ARC下,很多时候系统帮我们完成了拷贝,具体什么时候,要实测。

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

3)栈block,类似局部变量,出了它的作用域,会被系统回收内存,再调用可能导致程序崩溃。 

4)堆block,正如其他Objective-C对象那样,利用引用计数进行内存管理。 

 

3、在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. */
};

 

5、循环引用

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下区别很大?

 

posted @ 2019-01-14 09:53  happyyoung  阅读(343)  评论(0编辑  收藏  举报