iOS 底层 day09 block 捕获OC对象
2020-09-01 本文已影响0人
望穿秋水小作坊
一、block 捕获 OC对象
1. 在 ARC 环境下,编译器会根据情况
自动将栈上
的 block 复制到堆上
,比如有以下四种情况
- block 作为
函数返回值
时 - 将 block 赋值给
__strong
指针时 - block 作为
Cocoa API
中方法名含有usingBlock
的方法参数时 - block 作为
GCD API
的方法参数时
2. 观察下面代码,person 在断点 1 和断点 2 位置被释放了吗?
#import <Foundation/Foundation.h>
#import "Person.h"
typedef void (^SPBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
SPBlock block1;
{
Person *person1 = [[Person alloc] init];
NSLog(@"person1,%p",person1);
block1 = ^{
NSLog(@"hello,%p",person1);
};
}
{
Person *person2 = [[Person alloc] init];
NSLog(@"person2,%p",person2);
^{
NSLog(@"hello,%p",person2);
};
}
NSLog(@"断点1");
}
NSLog(@"断点2");
return 0;
}
- 日志情况如下
Demo[3570:263110] person1,0x1006acd90
Demo[3570:263110] person2,0x1006acc50
Demo[3570:263110] Person - dealloc<Person: 0x1006acc50>
Demo[3570:263110] 断点1
Demo[3570:263110] Person - dealloc<Person: 0x1006acd90>
Demo[3570:263110] 断点2
- 我们可以发现,
断点1
处person2
已经被释放了,而person1
活到了断点 2
位置 - 这是为什么呢?
- 这两处代码,不同之处在于
block1
有强引用,所以block1
会在ARC
下会进行一次 copy 操作,然后从栈 block
copy成堆 block
,在 copy 的时候会强引用person1
- 而另一个 block 没有强引用,一直是
栈 block
,作为栈 block
,自己都随时不保,更不能保住person2
3. 观察下面代码,Person对象在断点 1 和断点 2 位置被释放了吗?
#import <Foundation/Foundation.h>
#import "Person.h"
typedef void (^SPBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
SPBlock block1;
SPBlock block2;
{
Person *person1 = [[Person alloc] init];
NSLog(@"person1:%p",person1);
block1 = ^{
NSLog(@"hello,%p",person1);
};
}
{
Person *person2 = [[Person alloc] init];
NSLog(@"person2:%p",person2);
__weak Person *weakPerson = person2;
block2 = ^{
NSLog(@"hello,%p",weakPerson);
};
}
NSLog(@"断点1");
}
NSLog(@"断点2");
return 0;
}
- 打印日志如下
Demo[4124:285733] person1:0x1005acf10
Demo[4124:285733] person2:0x1005b9c50
Demo[4124:285733] Person - dealloc<Person: 0x1005b9c50>
Demo[4124:285733] 断点1
Demo[4124:285733] Person - dealloc<Person: 0x1005acf10>
Demo[4124:285733] 断点2
4. 为了观察例子3
的本质,我们使用如下指令,将上述代码转换成 C++代码
- 指令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
- 然后我们主要观察如下代码
// block1 对应结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
Person *__strong person1;
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->person1, (void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->person1, 3/*BLOCK_FIELD_IS_OBJECT*/);}
// block2 对应结构体
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
Person *__weak weakPerson;
};
static struct __main_block_desc_1 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_1*, struct __main_block_impl_1*);
void (*dispose)(struct __main_block_impl_1*);
}
static void __main_block_copy_1(struct __main_block_impl_1*dst, struct __main_block_impl_1*src) {_Block_object_assign((void*)&dst->weakPerson, (void*)src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_1(struct __main_block_impl_1*src) {_Block_object_dispose((void*)src->weakPerson, 3/*BLOCK_FIELD_IS_OBJECT*/);}
- 我们可以发现如下内容
- ①person1 在外部是
__strong
,生成结构体内部也是__strong
- ②person2 在外部是
__weak
,生成的结构体内部也是__weak
- ③相比上一小节,
__main_block_desc
多了__main_block_copy
和__main_block_dispose
两个函数 - ④当 block 被拷贝到堆上时,内部会自动调用
__main_block_copy
- ⑤当 block 离开作用域被释放时,内部会自动调用
__main_block_dispose
- ⑥
_Block_object_assign
函数会根据auto
变量的修饰符(__strong、__weak、 _unsafe_unretained)
做出相应的操作,形成强引用、弱引用
- ⑦ block 内部就是通过上述的内容,来维持 ARC 引用计数器的平衡
5. 通过练习巩固上面的知识,请问下面的 Person对象多少秒后释放?
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person1 = [[Person alloc] init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"person1:%p", person1);
});
Person *person2 = [[Person alloc] init];
__weak Person *weakPerson2 = person2;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"weakPerson2:%p", weakPerson2);
});
Person *person3 = [[Person alloc] init];
__weak Person *weakPerson3 = person3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"weakPerson3:%p", weakPerson3);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"person3:%p", person3);
});
});
NSLog(@"touchesBegan");
}
- 输出日志如下
14:58:57.708776 Demo[4756:326199] touchesBegan
14:58:57.709004 Demo[4756:326199] Person - dealloc:<Person: 0x6000020ec2d0>
14:58:58.708922 Demo[4756:326199] person1:0x6000020ec2c0
14:58:58.709254 Demo[4756:326199] Person - dealloc:<Person: 0x6000020ec2c0>
14:58:59.859905 Demo[4756:326199] weakPerson2:0x0
14:58:59.860169 Demo[4756:326199] weakPerson3:0x6000020ec2e0
14:59:00.959406 Demo[4756:326199] person3:0x6000020ec2e0
14:59:00.959724 Demo[4756:326199] Person - dealloc:<Person: 0x6000020ec2e0>
-
person1
经过1s
被释放 -
person2
点击完,立刻被释放 -
person3
经过3s
被释放