iOS Block

一、概述

Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block

二、本质

为了探究Block的本质,新建一个Command Line Tool 工程,声明一个简单的Block。

#import 

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^JDPBlock) (void) = ^{printf("Hello JDPBlock");};
    }
    return 0;
}

cd 到源文件目录,rewrite

clang -rewrite-objc main.m

得到文件main.cpp

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Hello JDPBlock");}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*JDPBlock) (void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    }
    return 0;
}

转换过程见此篇,不赘述

三、MRC

在MRC下根据存放位置的不同,Block分为三类:NSGlobalBlock、NSStackBlock、NSMallocBlock

1、NSGlobalBlock

没有访问栈变量的Block都为NSGlobalBlock
输出字符串常量:

- (void)testGlobalBlock {
    void (^GlobalBlock)(void) = ^() {
        NSLog(@"Hello Block");
    };
    NSLog(@"%@", GlobalBlock);
    GlobalBlock();
}

2019-12-20 10:28:29.663904+0800 BlockBlog[1297:57162] <__NSGlobalBlock__: 0x1031cf070>
2019-12-20 10:28:31.768400+0800 BlockBlog[1297:57162] Hello Block

输出全局变量:

NSInteger GlobalVar = 1;

- (void)testGlobalBlock {
    void (^GlobalBlock)(void) = ^() {
        NSLog(@"%ld", GlobalVar);
    };
    NSLog(@"%@", GlobalBlock);
    GlobalBlock();
}

2019-12-20 10:31:39.861782+0800 BlockBlog[1435:68044] <__NSGlobalBlock__: 0x101010070>
2019-12-20 10:31:41.976316+0800 BlockBlog[1435:68044] 1

2、NSStackBlock

访问栈变量的Block为NSStackBlock
输出局部变量:

- (void)testStackBlock {
    NSInteger stackVar = 2;
    void (^StackBlock)(void) = ^() {
        NSLog(@"%ld", stackVar);
    };
    NSLog(@"%@", StackBlock);
    StackBlock();
}

2019-12-20 10:58:13.224019+0800 BlockBlog[2153:130901] <__NSStackBlock__: 0x7ffee6bb2088>
2019-12-20 10:58:16.148405+0800 BlockBlog[2153:130901] 2

2.1、NSStackBlock访问基本数据类型

修饰符__block对block内部基本类型变量的影响

2.1.1、不加__block修饰符

- (void)testStackBlock {
    NSInteger stackVar = 2;
    NSLog(@"stackVar address:%p", &stackVar);
    void (^StackBlock)(void) = ^() {
        NSLog(@"stackVar(Block) address:%p", &stackVar);
    };
    StackBlock();
    NSLog(@"%@", StackBlock);
}

2019-12-20 11:04:11.002604+0800 BlockBlog[2411:152313] stackVar address:0x7ffee4f770b8
2019-12-20 11:04:11.002848+0800 BlockBlog[2411:152313] stackVar(Block) address:0x7ffee4f770a8
2019-12-20 11:04:11.002949+0800 BlockBlog[2411:152313] <__NSStackBlock__: 0x7ffee4f77088>

block对不加__block修饰符的基本数据类型变量进行了拷贝,内外访问不是一块内存地址。和C语言基本数据类型做函数形参属于简单的值传递有些相似。
1、block被转换为结构体类型,并增加了一个截获变量类型的成员变量。
2、block定义时,调用了block结构体的构造函数,对基本数据类型变量进行了函数形参的值传递,保存到block结构体实例的成员变量中。
3、block定义后,局部变量值的修改不会影响到block结构体实例的成员变量的值
4、block执行时,访问截获的变量其实是访问的block结构体实例的成员变量。

2.1.2、添加__block修饰符

- (void)testStackBlock {
    __block NSInteger stackVar = 2;
    NSLog(@"stackVar address:%p", &stackVar);
    void (^StackBlock)(void) = ^() {
        NSLog(@"stackVar(Block) address:%p", &stackVar);
    };
    StackBlock();
    NSLog(@"%@", StackBlock);
}

2019-12-20 11:35:48.956734+0800 BlockBlog[3232:209066] stackVar address:0x7ffee03600b8
2019-12-20 11:35:54.761303+0800 BlockBlog[3232:209066] stackVar(Block) address:0x7ffee03600b8
2019-12-20 11:35:59.755943+0800 BlockBlog[3232:209066] <__NSStackBlock__: 0x7ffee0360060>

对添加__block修饰符的变量,其地址会被拷贝置前述的内存区中,内外访问的是一块内存地址,与C语言的指针类型作函数参数相似。
1、添加了__block修饰符的变量,将被转换成结构体类型。
2、在__block变量定义时,调用了结构体的构造函数,将值保存在了结构体实例的成员变量中
3、在其后的block结构体定义和执行时,都是访问的__block结构体实例的成员变量。

2.1.3、添加__block修饰符验证

- (void)testStackBlock {
    __block NSInteger stackVar = 2;
    NSLog(@"stackVar:%ld", stackVar);
    NSLog(@"stackVar address:%p", &stackVar);
    void (^StackBlock)(void) = ^() {
        stackVar++;
        NSLog(@"stackVar(Block):%ld", stackVar);
        NSLog(@"stackVar(Block) address:%p", &stackVar);
    };
    StackBlock();
    stackVar++;
    NSLog(@"stackVar:%ld", stackVar);
    NSLog(@"stackVar address:%p", &stackVar);
    NSLog(@"%@", StackBlock);
}

2019-12-20 11:49:13.427384+0800 BlockBlog[3604:239956] stackVar:2
2019-12-20 11:49:13.856289+0800 BlockBlog[3604:239956] stackVar address:0x7ffeec6bf0b8
2019-12-20 11:49:15.723084+0800 BlockBlog[3604:239956] stackVar(Block):3
2019-12-20 11:49:16.217291+0800 BlockBlog[3604:239956] stackVar(Block) address:0x7ffeec6bf0b8
2019-12-20 11:49:18.984754+0800 BlockBlog[3604:239956] stackVar:4
2019-12-20 11:49:19.829138+0800 BlockBlog[3604:239956] stackVar address:0x7ffeec6bf0b8
2019-12-20 11:49:20.874982+0800 BlockBlog[3604:239956] <__NSStackBlock__: 0x7ffeec6bf060>

2.2、NSStackBlock访问对象类型

因为对象类型需要研究的点较多,预定义宏:

#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@_引用计数:%ld", Comment, &Obj, Obj, Obj, [Obj retainCount]);

2.2.1、不加__block修饰符

- (void)testStackBlock {
    NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    void (^StackBlock)(void) = ^() {
        MyLog(@"Block Object", object);
    };
    StackBlock();
    MyLog(@"StackBlock", StackBlock);
    [object release];
}

2019-12-20 12:05:43.619042+0800 BlockBlog[4079:270166] 注释:Object_指针地址:0x7ffee48060b8_对象地址:0x60000097a780_对象:_引用计数:1
2019-12-20 12:05:43.619267+0800 BlockBlog[4079:270166] 注释:Block Object_指针地址:0x7ffee48060a8_对象地址:0x60000097a780_对象:_引用计数:1
2019-12-20 12:05:43.619366+0800 BlockBlog[4079:270166] 注释:StackBlock_指针地址:0x7ffee48060b0_对象地址:0x7ffee4806088_对象:<__NSStackBlock__: 0x7ffee4806088>_引用计数:1

block对不加__block修饰符的对象类型变量增加了一个指针指向,但并没有增加对象的引用计数。
block结构体类型新增一个NSObject类型的成员变量,定义时,调用构造函数传递指针类型参数,访问的是同一个内存地址。

2.1.2、添加__block修饰符

- (void)testStackBlock {
    __block NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    void (^StackBlock)(void) = ^() {
        MyLog(@"Block Object", object);
    };
    StackBlock();
    MyLog(@"StackBlock", StackBlock);
    [object release];
}

2019-12-20 12:17:34.652867+0800 BlockBlog[4391:290717] 注释:Object_指针地址:0x7ffeea1a20b8_对象地址:0x60000237c270_对象:_引用计数:1
2019-12-20 12:17:35.762576+0800 BlockBlog[4391:290717] 注释:Block Object_指针地址:0x7ffeea1a20b8_对象地址:0x60000237c270_对象:_引用计数:1
2019-12-20 12:17:38.476372+0800 BlockBlog[4391:290717] 注释:StackBlock_指针地址:0x7ffeea1a2078_对象地址:0x7ffeea1a2050_对象:<__NSStackBlock__: 0x7ffeea1a2050>_引用计数:1

对添加__block修饰符的变量,内外使用的是一个指针。
1、添加了__block修饰符的变量,被转换成了结构体类型,并包含一个NSObject类型的成员变量,调用构造函数时,将引用保存到了成员变量中。
2、在block结构体实例内、外访问的__block结构体实例是同一个,其成员变量的引用也相同。

3、NSMallocBlock

对NSStackBlock执行Block_copy()或者copy方法,将会得到一个存在heap的block的引用。注:原来的NSStackBlock在作用域内依然存在。

3.1、NSMallocBlock访问基本数据类型

- (void)testMallocBlock {
    NSInteger stackVar = 2;
    NSLog(@"stackVar:%ld", stackVar);
    NSLog(@"stackVar address:%p", &stackVar);
    void (^StackBlock)(void) = ^() {
        NSLog(@"stackVar(Block):%ld", stackVar);
        NSLog(@"stackVar(Block) address:%p", &stackVar);
    };
    StackBlock();
    MyLog(@"StackBlock", StackBlock);
    
    void (^MallocBlock)(void) = Block_copy(StackBlock);
    MyLog(@"StackBlock", StackBlock);
    MyLog(@"MallocBlock", MallocBlock);
    MallocBlock();
    NSLog(@"stackVar:%ld", stackVar);
    NSLog(@"stackVar address:%p", &stackVar);
}

2019-12-22 15:08:08.973506+0800 BlockBlog[4841:221535] stackVar:2
2019-12-22 15:08:08.973714+0800 BlockBlog[4841:221535] stackVar address:0x7ffeee82f0b8
2019-12-22 15:08:10.378907+0800 BlockBlog[4841:221535] stackVar(Block):2
2019-12-22 15:08:10.379114+0800 BlockBlog[4841:221535] stackVar(Block) address:0x7ffeee82f0a8
2019-12-22 15:08:10.379275+0800 BlockBlog[4841:221535] 注释:StackBlock_指针地址:0x7ffeee82f0b0_对象地址:0x7ffeee82f088_对象:<__NSStackBlock__: 0x7ffeee82f088>_引用计数:1
2019-12-22 15:08:10.379379+0800 BlockBlog[4841:221535] 注释:StackBlock_指针地址:0x7ffeee82f0b0_对象地址:0x7ffeee82f088_对象:<__NSStackBlock__: 0x7ffeee82f088>_引用计数:1
2019-12-22 15:08:10.379473+0800 BlockBlog[4841:221535] 注释:MallocBlock_指针地址:0x7ffeee82f080_对象地址:0x600003674570_对象:<__NSMallocBlock__: 0x600003674570>_引用计数:1
2019-12-22 15:08:12.258836+0800 BlockBlog[4841:221535] stackVar(Block):2
2019-12-22 15:08:12.258965+0800 BlockBlog[4841:221535] stackVar(Block) address:0x600003674590
2019-12-22 15:08:12.259034+0800 BlockBlog[4841:221535] stackVar:2
2019-12-22 15:08:12.259105+0800 BlockBlog[4841:221535] stackVar address:0x7ffeee82f0b8

3.2、NSMallocBlock访问对象类型

3.2.1、不加__block修饰符

- (void)testMallocBlock {
    NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    void (^StackBlock)(void) = ^() {
        MyLog(@"Block Object", object);
    };
    MyLog(@"StackBlock", StackBlock);
    StackBlock();
    
    void (^MallocBlock)(void) = Block_copy(StackBlock);
    MyLog(@"Object", object);
    MyLog(@"MallocBlock", MallocBlock);
    MallocBlock();
    Block_release(MallocBlock);
    MyLog(@"Object", object);
    [object release];
}
2019-12-22 15:16:07.725862+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:1
2019-12-22 15:16:08.896491+0800 BlockBlog[5066:245483] 注释:StackBlock_指针地址:0x7ffeeefd30b0_对象地址:0x7ffeeefd3088_对象:<__NSStackBlock__: 0x7ffeeefd3088>_引用计数:1
2019-12-22 15:16:09.863981+0800 BlockBlog[5066:245483] 注释:Block Object_指针地址:0x7ffeeefd30a8_对象地址:0x6000016065d0_对象:_引用计数:1
2019-12-22 15:16:11.224562+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:2
2019-12-22 15:16:11.719379+0800 BlockBlog[5066:245483] 注释:MallocBlock_指针地址:0x7ffeeefd3080_对象地址:0x600001a5f9c0_对象:<__NSMallocBlock__: 0x600001a5f9c0>_引用计数:1
2019-12-22 15:16:12.811445+0800 BlockBlog[5066:245483] 注释:Block Object_指针地址:0x600001a5f9e0_对象地址:0x6000016065d0_对象:_引用计数:2
2019-12-22 15:16:27.617261+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:_引用计数:1

对block执行Block_copy后,不加__block修饰符的对象类型变量的引用计数增加为2,对block执行Block_release后,对象的引用计数又减为1。

3.2.1、添加__block修饰符

- (void)testMallocBlock {
    __block NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    void (^StackBlock)(void) = ^() {
        MyLog(@"Block Object", object);
    };
    MyLog(@"StackBlock", StackBlock);
    StackBlock();
    
    void (^MallocBlock)(void) = Block_copy(StackBlock);
    MyLog(@"Object", object);
    MyLog(@"MallocBlock", MallocBlock);
    MallocBlock();
    Block_release(MallocBlock);
    MyLog(@"Object", object);
    [object release];
}

2019-12-22 15:18:17.232005+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x7ffee48da0b8_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:18:22.586181+0800 BlockBlog[5137:251392] 注释:StackBlock_指针地址:0x7ffee48da078_对象地址:0x7ffee48da050_对象:<__NSStackBlock__: 0x7ffee48da050>_引用计数:1
2019-12-22 15:18:26.264866+0800 BlockBlog[5137:251392] 注释:Block Object_指针地址:0x7ffee48da0b8_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:18:48.024314+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:19:07.104200+0800 BlockBlog[5137:251392] 注释:MallocBlock_指针地址:0x7ffee48da048_对象地址:0x600001b7e3a0_对象:<__NSMallocBlock__: 0x600001b7e3a0>_引用计数:1
2019-12-22 15:19:09.545584+0800 BlockBlog[5137:251392] 注释:Block Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1
2019-12-22 15:19:28.345517+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:_引用计数:1

对添加了__block修饰符的对象类型变量,对block执行Block_copy后指针转移到了heap中,Block_copy和 Block_release对对象的引用计数没有影响。

3.3、MRC下的循环引用问题

@property (nonatomic, copy) void (^iVarBlock) (void);

- (void)testCycleReference {
    MyLog(@"self", self);
    self.iVarBlock = ^{
        // self
        MyLog(@"self", self);
        // ivar
        // MyLog(@"self", _title);
        // method
        // MyLog(@"self", [self description]);
    };
    MyLog(@"Block", _iVarBlock);
}

2019-12-22 16:45:26.626182+0800 BlockBlog[7592:349282] 注释:self_指针地址:0x7ffee076a0c8_对象地址:0x7ff5add35ff0_对象:_引用计数:3
2019-12-22 16:45:26.626392+0800 BlockBlog[7592:349282] 注释:Block_指针地址:0x7ff5add36340_对象地址:0x600000b190e0_对象:<__NSMallocBlock__: 0x600000b190e0>_引用计数:1

在block中直接访问self、访问self的成员变量和成员方法,都会导致self的引用计数加1。
如果self又同时引用了block,则会导致循环引用。

解决方法:为self添加一个__block修饰符

- (void)testCycleReference {
    __block typeof(self) blockSelf = self;
    MyLog(@"self", blockSelf);
    self.iVarBlock = ^{
        // self
        MyLog(@"self", blockSelf);
        // ivar
        // MyLog(@"self", _title);
        // method
        // MyLog(@"self", [self description]);
    };
    MyLog(@"Block", _iVarBlock);
}

四、ARC

在ARC下,Block同样分为三类:NSGlobalBlock、NSStackBlock、NSMallocBlocks
修改宏定义:

#if __has_feature(objc_arc)
#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@", Comment, &Obj, Obj, Obj);
#else
#define MyLog(Comment, Obj) NSLog(@"注释:%@_指针地址:%p_对象地址:%p_对象:%@_引用计数:%ld", Comment, &Obj, Obj, Obj, [Obj retainCount]);
#endif

1、NSGlobalBlock

与MRC没有什么差异。

2、NSStackBlock

2.1、Block类型作为属性被assign修饰时

@property (nonatomic, assign) void (^iVarBlock) (void);

- (void)testStackBlock {
    NSInteger stackVar = 2;
    self.iVarBlock = ^{
        NSLog(@"stackVar:%ld", stackVar);
    };
    MyLog(@"Block", _iVarBlock);
    _iVarBlock();
}

2019-12-22 17:22:38.912053+0800 BlockBlog[8668:419567] 注释:Block_指针地址:0x7ffde6c0e380_对象地址:0x7ffeebc81080_对象:<__NSStackBlock__: 0x7ffeebc81080>

2.2、Block类型作为函数参数时

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    NSInteger stackVar = 2;
    [self testStackBlock:^{
        NSLog(@"stackVar:%ld", stackVar);
    }];
}

- (void)testStackBlock:(void (^) (void))stackBlock {
    MyLog(@"Block", stackBlock);
    stackBlock();
}

2019-12-22 17:36:07.119089+0800 BlockBlog[9102:457220] 注释:Block_指针地址:0x7ffee1207078_对象地址:0x7ffee12070b0_对象:<__NSStackBlock__: 0x7ffee12070b0>
2019-12-22 17:36:10.021307+0800 BlockBlog[9102:457220] stackVar:2

3、NSMallocBlock

3.1、NSMallocBlock访问基本数据类型

3.1.1、不加__block修饰符

- (void)testMallocBlock {
    NSInteger stack_var = 1;
    NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
    void (^MallocBlock)(void) = ^() {
        NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
    };
    MyLog(@"MallocBlock", MallocBlock);
    MallocBlock();
    NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
    stack_var ++;
    NSLog(@"stack_var:%ld stack_var Address:%p", stack_var, &stack_var);
    MallocBlock();
}

2019-12-22 17:43:18.607711+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:43:26.336411+0800 BlockBlog[9319:471777] 注释:MallocBlock_指针地址:0x7ffee86fd0a0_对象地址:0x6000026796b0_对象:<__NSMallocBlock__: 0x6000026796b0>
2019-12-22 17:43:33.391549+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x6000026796d0
2019-12-22 17:44:07.861631+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:44:11.585388+0800 BlockBlog[9319:471777] stack_var:2 stack_var Address:0x7ffee86fd0a8
2019-12-22 17:44:16.120562+0800 BlockBlog[9319:471777] stack_var:1 stack_var Address:0x6000026796d0

3.2、NSMallocBlock访问对象类型

3.2.1、添加__block修饰符

- (void)testMallocBlock {
    NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    __block NSObject *blockObject = object;
    MyLog(@"blockObject", blockObject);
    void (^MallocBlock)(void) = ^ {
        MyLog(@"Block blockObj", blockObject);
    };
    object = nil;
    MyLog(@"Object", object);
    MallocBlock();
    MyLog(@"%@", MallocBlock);
}

2019-12-22 18:10:19.713250+0800 BlockBlog[10140:528772] 注释:Object_指针地址:0x7ffeef24d0a8_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713507+0800 BlockBlog[10140:528772] 注释:blockObject_指针地址:0x7ffeef24d0a0_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713592+0800 BlockBlog[10140:528772] 注释: Object_指针地址:0x7ffeef24d0a8_对象地址:0x0_对象:(null)
2019-12-22 18:10:19.713707+0800 BlockBlog[10140:528772] 注释:Block blockObj_指针地址:0x600000b48e38_对象地址:0x600000714f70_对象:
2019-12-22 18:10:19.713794+0800 BlockBlog[10140:528772] 注释:%@_指针地址:0x7ffeef24d060_对象地址:0x600000b48c30_对象:<__NSMallocBlock__: 0x600000b48c30>

当外部 object 指向 nil 的时候,object 理应被释放,但实际上 blockObject 依然强引用着 object,object 其实并没有被真正释放。因此ARC下的__block并不能阻止block对访问对象引用计数的增加。

3.2.2、添加__weak修饰符

- (void)testMallocBlock {
    NSObject *object = [[NSObject alloc] init];
    MyLog(@"Object", object);
    __weak NSObject * weakObj = object;
    MyLog(@"weakObject", weakObj);
    void (^MallocBlock)(void) = ^() {
        MyLog(@"Block weakObject", weakObj);
    };
    object = nil;
    MyLog(@"Object", object);
    MallocBlock();
    MyLog(@"%@", MallocBlock);
}

2019-12-22 18:12:24.583423+0800 BlockBlog[10206:537851] 注释:Object_指针地址:0x7ffeec0b90a8_对象地址:0x600000ae03b0_对象:
2019-12-22 18:12:27.815357+0800 BlockBlog[10206:537851] 注释:weakObject_指针地址:0x7ffeec0b90a0_对象地址:0x600000ae03b0_对象:
2019-12-22 18:12:35.383486+0800 BlockBlog[10206:537851] 注释:Object_指针地址:0x7ffeec0b90a8_对象地址:0x0_对象:(null)
2019-12-22 18:12:38.105942+0800 BlockBlog[10206:537851] 注释:Block weakObject_指针地址:0x6000006a63f0_对象地址:0x0_对象:(null)
2019-12-22 18:12:41.872538+0800 BlockBlog[10206:537851] 注释:%@_指针地址:0x7ffeec0b9088_对象地址:0x6000006a63d0_对象:<__NSMallocBlock__: 0x6000006a63d0>

当object赋值 nil 时,block 内部的 weakObject 也为 nil 了,也就是说 object 实际上是被释放了。

你可能感兴趣的:(iOS Block)