前面的block讲解中,block访问的都是基本类型的变量,接下来我们了解一下block访问对象类型的auto变量.
image访问对象类型auto变量,转换后的C++代码如下:
void(*block)(void);
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
TestClass *__strong testClass;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, TestClass *__strong _testClass, int flags=0) : testClass(_testClass) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
TestClass *__strong testClass = __cself->testClass; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders__6_s9x0n6313d99yqk5pltzp6ym0000gn_T_main_c36270_mi_0,(long)((NSInteger (*)(id, SEL))(void *)objc_msgSend)((id)testClass, sel_registerName("age")));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->testClass, (void*)src->testClass, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->testClass, 3/*BLOCK_FIELD_IS_OBJECT*/);}
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};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
TestClass *testClass = ((TestClass *(*)(id, SEL))(void *)objc_msgSend)((id)((TestClass *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("TestClass"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)testClass, sel_registerName("setAge:"), (NSInteger)20);
block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, testClass, 570425344));
((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)testClass, sel_registerName("setAge:"), (NSInteger)25);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
对比访问基本数据类型:
相同点:block结构体内部也存在一个与被访问的变量同名的成员变量,本demo中,也就是TestClass *__strong testClass
;即testClass实例。
不同点:__main_block_desc_0
结构体的实现,发现其内部增加了两个函数:
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
补充:关于copy函数与dispose函数的调用机制
如果block被拷贝到堆上,会调用block内部的copy函数。copy函数内部会调用
_Block_object_assign
函数,_Block_object_assign
函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained
)做出相应的操作,形成强引用(retain
)或者弱引用。
如果block从堆上移除,会调用block内部的dispose函数。dispose函数内部会调用
_Block_object_dispose
函数,_Block_object_dispose
函数会自动释放引用的 auto变量(release
)。
所谓的弱引用就是在变量前面加上“__weak
”修饰符,默认是"__strong
"修饰的,即默认对变量为强引用。创建testClass实例加上“__weak
”前缀,__weak __weak TestClass *weakTestClass = testClass
;对应C++代码中,block结构体内部的成员testClass就声明成 TestClass *__weak weakTestClass
;
在使用clang将OC代码转换为C++代码时,可能会遇到以下问题:cannot create __weak reference in file using manual reference
解决方案:支持ARC、指定运行时系统版本,比如xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
MRC下没有"强引用"的概念,只有retain、release
概念。栈空间block不会保住引用对象的命,即不会对引用对象进行强引用。但是堆空间block通过强引用被访问对象,从而延长被引用对象的生命周期。
所以可以对栈内存的block进行copy
操作,变成堆内存上的block,这样被block引用的对象生命周期就会保住,等到block销毁的时候,被引用的对象才会销毁。
不管ARC、MRC,栈空间block都不会持有对象,如果是堆空间block,有能力保住被引用对象的命,换成ARC下的说法就是“强引用”,MRC下没有“强引用”。
在ARC下,我们通过demoA、demoB看看以往开发中,在堆上block内访问oc对象使用不同修饰符造成的结果:
demoA:
imagedemoB:
imagedemoB中,testClass对象默认被__strong
修饰符修饰,block会对其强引用,转换为C++环境后block结构体内容如下:
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
TestClass *__strong testClass;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, TestClass *__strong _testClass, int flags=0) : testClass(_testClass) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block结构体内成员变量testClass就是就是OC中alloc出来的testClass对象,由于该block在堆上,在当前情况下,只要该block没有被销毁,block的内存就一直存在,其内部成员变量testClass由于被"__strong"强引用修饰,也会一直存在,故而TestClass类也一直存在于内存中,这在很多情况下会成为App内存泄露的根源。
总结:
image当block内部访问了对象类型的auto变量时
- 如果block是在栈上,将不会对auto变量产生强引用
- 如果block是在堆上(copy到栈上时__main_block_desc_0增加了copy和dipose函数)
1. 会调用block内部的copy函数
2. copy函数内部会调用_Block_object_assign函数
3. _Block_object_assign函数会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(对atuo变量)- 如果block从堆上移除
1. 会调用block内部的dispose函数
2. dispose函数内部会调用_Block_object_dispose函数
3. _Block_object_dispose函数会自动释放引用的auto变量(release)
网友评论