对象A持有对象B,调用B的block参数方法,在里面使用了self。在使用block我们都会默认在里面使用weakself,网上搜了很多解释都是为了防止循环引用,以防self被持有导致内存泄露。
那么问题来了,到底是谁持有了self?我以前没有深究,一直以为是A和B互相持有导致的循环引用。
但是最近正好有重新深究block,于是便想着从block里找到答案。首先写一段代码,将其从oc转换成c++
@implementation C
-(void)test {}
-(void) methodA{
[self methodB:^{
[self test];
}];
}
-(void):(void(^)(void))handler {
handler();
}
@end
clang -rewrite-objc xx.m
查看转换后的代码,可以看到,block就是一个结构体,因为在里面使用了self,所以结构体就增加了一个C *self。methodA方法里调用了methodB方法,创建了一个block结构体变量作为参数传了进去,就是这个结构体变量持有了self。
相当于A调用B的block方法,里面使用了self,创建了一个临时的block变量持有self,self不是被B持有,而是被一个临时变量持有。正常情况下,methodB调用完毕后,这个block变量自然就会被释放,self也就不再被它持有了。但有些情况就是,这个block变量会被保留下来,从而导致self内存泄露的原因。
//block结构体
struct __C__methodA_block_impl_0 {
struct __block_impl impl;
struct __C__methodA_block_desc_0* Desc;
C *self;
__C__methodA_block_impl_0(void *fp, struct __C__methodA_block_desc_0 *desc, C *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//methodA
static void _I_C_methodA(C * self, SEL _cmd) {
((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("methodB:"), ((void (*)())&__C__methodA_block_impl_0((void *)__C__methodA_block_func_0, &__C__methodA_block_desc_0_DATA, self, 570425344)));
}
//methodB
static void _I_C_methodB_(C * self, SEL _cmd, void (*handler)()) {
((void (*)(__block_impl *))((__block_impl *)handler)->FuncPtr)((__block_impl *)handler);
}
具体有两种情况会导致self内存泄露:
1.循环引用,所谓的循环引用不是A<->B互相引用,而是A->B->block->A。
2.持有block的对象存活时间要比self长,比如类对象或者单例对象等。
所以建议使用block还是要weakself,因为你无法保证传过去的block是否在方法的另一边被保留下来了。比如常见的NSTimer,就是因为self被block持有,block被NSTimer类对象持有。
下面几种block情况:
@implementation A
- (instancetype)initWithName:(NSString *)name {
if (self = [super init]) {
_name = name;
}
return self;
}
- (void)dealloc {
NSLog(@"%@ dealloc", self.name);
}
//内存释放
//调用B的block参数方法,B不保存block,A不保存B
- (void)B_Method1 {
B *b = [[B alloc] init];
[b method1:^{
[self func];
NSLog(@"invoke B Method1, but dont retain B");
}];
}
//内存释放
//调用B的block参数方法,B保存block,A不保存B
- (void)B_Method2 {
B *b = [[B alloc] init];
[b method2:^{
[self func];
NSLog(@"invoke B Method2, but dont retain B");
}];
}
//内存释放
//调用B的block参数方法,B不保存block,A保存B对象
- (void)B_Method1_RetainB {
B *b = [[B alloc] init];
[b method1:^{
[self func];
NSLog(@"invoke B Method1, and retain B");
}];
self.b = b;
}
//内存泄露
//调用B的block参数方法,B保存block,A保存B对象
- (void)B_Method2_RetainB {
B *b = [[B alloc] init];
[b method2:^{
[self func];
NSLog(@"invoke B Method2, and retain B");
}];
self.b = b;
}
//内存释放
//调用B的block参数 类方法,B不保存block
- (void)B_ClassMethod1 {
[B classMethod1:^{
[self func];
NSLog(@"invoke B ClassMethod1");
}];
}
//内存泄露
//调用B的block参数 类方法,B保存block
- (void)B_ClassMethod2 {
[B classMethod2:^{
[self func];
NSLog(@"invoke B ClassMethod1");
}];
}
- (void)func {
}
@end
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)