iOS block循环引用问题深究

iOS block循环引用问题深究,第1张

        对象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

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/989523.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存