iOS Block

一、概述

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

二、本质

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

#import <Foundation/Foundation.h>

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_对象:<NSObject: 0x60000097a780>_引用计数:1
2019-12-20 12:05:43.619267+0800 BlockBlog[4079:270166] 注释:Block Object_指针地址:0x7ffee48060a8_对象地址:0x60000097a780_对象:<NSObject: 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_对象:<NSObject: 0x60000237c270>_引用计数:1
2019-12-20 12:17:35.762576+0800 BlockBlog[4391:290717] 注释:Block Object_指针地址:0x7ffeea1a20b8_对象地址:0x60000237c270_对象:<NSObject: 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_对象:<NSObject: 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_对象:<NSObject: 0x6000016065d0>_引用计数:1
2019-12-22 15:16:11.224562+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:<NSObject: 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_对象:<NSObject: 0x6000016065d0>_引用计数:2
2019-12-22 15:16:27.617261+0800 BlockBlog[5066:245483] 注释:Object_指针地址:0x7ffeeefd30b8_对象地址:0x6000016065d0_对象:<NSObject: 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_对象:<NSObject: 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_对象:<NSObject: 0x6000017381e0>_引用计数:1
2019-12-22 15:18:48.024314+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:<NSObject: 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_对象:<NSObject: 0x6000017381e0>_引用计数:1
2019-12-22 15:19:28.345517+0800 BlockBlog[5137:251392] 注释:Object_指针地址:0x600001b7e638_对象地址:0x6000017381e0_对象:<NSObject: 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_对象:<ShowViewController: 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<UITouch *> *)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_对象:<NSObject: 0x600000714f70>
2019-12-22 18:10:19.713507+0800 BlockBlog[10140:528772] 注释:blockObject_指针地址:0x7ffeef24d0a0_对象地址:0x600000714f70_对象:<NSObject: 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_对象:<NSObject: 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_对象:<NSObject: 0x600000ae03b0>
2019-12-22 18:12:27.815357+0800 BlockBlog[10206:537851] 注释:weakObject_指针地址:0x7ffeec0b90a0_对象地址:0x600000ae03b0_对象:<NSObject: 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 实际上是被释放了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容

  • Block概要 Block:带有自动变量的匿名函数。 匿名函数:没有函数名的函数,一对{}包裹的内容是匿名函数的作...
    zweic阅读 505评论 0 2
  • 第一部分:Block本质 Q:什么是Block,Block的本质是什么? block本质上也是一个OC对象,它内部...
    sheldon_龙阅读 557评论 0 0
  • 参考篇:iOS-Block浅谈 前言:本文简述Block本质,如有错误请留言指正。 第一部分:Block本质 Q:...
    梦蕊dream阅读 61,202评论 41 322
  • 实质 block 实质是一个OC对象,也存在 isa 指针 1.底层实现 先看一个最简单的block例子: 使用 ...
    skogt阅读 491评论 0 3
  • 前言:Block 是开发过程中常用便捷的回调方式,本文简单介绍 Block 一、Block 简介 Block 对象...
    梦蕊dream阅读 4,753评论 5 26