前面已经说过Block对基本类型变量的捕获,现在来看看Block对对象变量的捕获。先看看下面几个问题
问题一
Person * person = [[Person alloc] init];
person.age = 10;
void (^block)(void) = ^{
NSLog(@"年龄:%ld",person.age);
};
person.age = 20;
block();
NSLog(@"%@",[block class]);
问题:
- 1、输出的年龄是多少?
- 2、block类型是什么?
答案:
- 1、输出的年龄为20
- 2、NSMallocBlock
1、对象的局部变量捕获和基本数据类型有点区别,对象的局部auto变量捕获是指针捕获不是值捕获,所以捕获的内容会受外部变量的影响
2、因为block捕获了auto变量所以block是NSStackBlock,在ARC下将block赋值给__strong指针时会自动将栈上的block复制到堆上,block由NSStackBlock变为NSMallocBlock
问题二
typedef void(^JTblock)(void);
JTblock block;
{
Person * person = [[Person alloc] init];
block = ^{
NSLog(@"%@",person);
};
}
block();
问题:
- 1、person什么时候被释放
- 2、block类型是什么?
答案:
- 1、 在执行完block()后被释放
- 2、NSMallocBlock
1、这里Person虽然是局部变量,但是不是在离开作用域时(离开大括号)被释放,因为Person在被block捕获时被block强引用了,引用计数加了1,只有当引用计数为0时才会被释放。当block调用完从堆上移除时,会调用block内部的dispose函数,dispose函数内部会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的auto变量(release),person的引用计数减1,此时person的引用计数为0被释放
2、block为NSMallocBlock的原因与问题一一样,这里需要说的是,只有在NSMallocBlock下才会对auto变量产生强引用,如果block是在栈上,将不会对auto变量产生强引用。
如果block被拷贝到堆上,会调用block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用
问题三
JTblock block;
{
Person * person = [[Person alloc] init];
__weak Person * weakPerson = person;
block = ^{
NSLog(@"%@",weakPerson);
};
}
block();
问题:
- Person什么时候被释放
答案:
- 离开大括号作用域时被释放
在问题二中有说到,堆上的Block会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用。这里的person的修饰符为weak,所以block会对其产生弱引用,引用计数不会加1,当出作用域时person会被释放
问题四
- (void)captureAutoVariable
{
self.age = 10;
void (^captureBlock)(void) = ^{
NSLog(@"age is %ld",self.age);
};
self.age = 20;
captureBlock();
}
问题:
- 输出的age是多少
答案:
- 20
1、self为对象局部auto变量,所以是指针捕获,内部的值会受外部的影响
2、block对self产生了一个强引用
- 在captureAutoVariable方法中没有什么参数,那self为什么是局部变量呢?
将oc代码转化为c++代码后发现,每个方法都会默认带上self和_cmd两个参数,所以在方法里调用self,其实是调用方法参数的self,那么self在方法中就是局部变量。
_I_Person_captureAutoVariable就是captureAutoVariable转化为c++代码后的方法名
static void _I_Person_captureAutoVariable(Person * self, SEL _cmd) {
((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("setAge:"), (NSInteger)10);
void (*captureBlock)(void) = ((void (*)())&__Person__captureAutoVariable_block_impl_0((void *)__Person__captureAutoVariable_block_func_0, &__Person__captureAutoVariable_block_desc_0_DATA, self, 570425344));
((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)self, sel_registerName("setAge:"), (NSInteger)20);
((void (*)(__block_impl *))((__block_impl *)captureBlock)->FuncPtr)((__block_impl *)captureBlock);
}