block的循环引用究竟是什么鬼?

block的循环引用到底是怎么回事?

我们先来看一段代码:

typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        simpleblock_ = ^{
            NSLog(@"self is %@",self);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

我们肯定不加思索的说这样造成了循环引用,因为self持有了block,block又持有了self,所以这个类不会得到释放,所以就会出现循环引用,造成内存泄露。可是,为什么这样呢,self持有block很好理解,可是block又是怎么持有了self了呢?下面做一下深入分析。

记得以前还没有使用ARC的时候,也就是使用MRC的时候,我们为了避免这种循环引用的情况,都是通过加__block来避免这种问题的,就是:

- (id)init
{
    self = [super init];
    if (self) {
        __block typeof(self) blockSelf = self;
        simpleblock_ = ^{
            NSLog(@"self is %@",self);
        };
    }
    return self;
}

后来使用ARC的时候,发现加__block不能避免这种问题了,改成使用__weak了,那么究竟为什么会这样,MRC和ARC究竟有什么不同,发生了什么呢。我们分别探究一下。

1.MRC的情况

我们在MRC的情况下,使用以下代码:

MRC下不加任何修饰:
typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        simpleblock_ = ^{
            NSLog(@"self is %@",self);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

使用clang进行反编译后,得到如下内容:


struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  BlockCase *self;
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, BlockCase *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockCase__init_block_func_0(struct __BlockCase__init_block_impl_0 *__cself) {
  BlockCase *self = __cself->self; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_c89a3d_mi_0,self);
}

static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __BlockCase__init_block_dispose_0(struct __BlockCase__init_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __BlockCase__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __BlockCase__init_block_impl_0*, struct __BlockCase__init_block_impl_0*);
  void (*dispose)(struct __BlockCase__init_block_impl_0*);
} __BlockCase__init_block_desc_0_DATA = { 0, sizeof(struct __BlockCase__init_block_impl_0), __BlockCase__init_block_copy_0, __BlockCase__init_block_dispose_0};

static id _I_BlockCase_init(BlockCase * self, SEL _cmd) {
    self = ((BlockCase *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("BlockCase"))}, sel_registerName("init"));
    if (self) {
        (*(simpleBlock *)((char *)self + OBJC_IVAR_$_BlockCase$simpleblock_)) = ((void (*)())&__BlockCase__init_block_impl_0((void *)__BlockCase__init_block_func_0, &__BlockCase__init_block_desc_0_DATA, self, 570425344));
    }
    return self;
}

int main()
{
    id myCase = ((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("BlockCase"), sel_registerName("alloc")), sel_registerName("init"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_c89a3d_mi_1, myCase);
    return 0;
}

之前在block深究这篇文章中,已经解释了一些基本的东西,这里就不一一解释,我们看到

 simpleblock_ = ^{
            NSLog(@"self is %@",self);
        };

这里发生了什么呢?simpleblock_是BlockCase的成员变量,因为BlockCase对象在堆上,simpleblock_也在堆上。^{NSLog(@”self = %@”, self);}这个block在栈上生成,因为赋值给堆上的simpleblock_,所以会被copy到堆上。那么你可能会问还有没有其他情况下,会把栈上的block复制到堆上呢?答案是有。

  • 调用block的copy实例方法的时候
  • block作为函数的返回值返回的时候
  • 将block赋值给附有__strong修饰符id类型的类或者block类型成员变量的时候
  • 在方法名中含有usingBlock和Cocoa框架方法或Grand Central Dispatch的API中传递block的时候

在上述例子代码中,我们直接使用self,么有对self进行任何修饰,但是在反编译的代码中,依然出现了辅助函数:

static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

之前在 block深究这篇文章中,我们知道block在复制到堆的时候会使用__BlockCase__init_block_copy_0辅助函数把自动变量从栈区赋值到堆里面,但是:

    id myCase = [[BlockCase alloc] init];

也就是myCase已经存储在堆里了,那么self已经在堆里面了,又是怎么copy的呢?
我们看一下源代码:

static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
        if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
            _Block_assign_weak(object, destAddr);
        }
        else {
            // do *not* retain or *copy* __block variables whatever they are
            _Block_assign((void *)object, destAddr);
        }
    }
    else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
        // copying a __block reference from the stack Block to the heap
        // flags will indicate if it holds a __weak reference and needs a special isa
        _Block_byref_assign_copy(destAddr, object, flags);
    }
    // (this test must be before next one)
    else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
        // copying a Block declared variable from the stack Block to the heap
        _Block_assign(_Block_copy_internal(object, flags), destAddr);
    }
    // (this test must be after previous one)
    else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        _Block_retain_object(object);
        _Block_assign((void *)object, destAddr);
    }
}

我们看到_Block_object_assign调用的时候传入第一个参数为self地址,第二个参数为对象self,第三个参数是类型 3/BLOCK_FIELD_IS_OBJECT/,匹配对对应的条件,执行代码为:

 else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        _Block_retain_object(object);
        _Block_assign((void *)object, destAddr);
    }

这里出现了_Block_retain_object(object);它表明被捕获的BlockCase对象被retain,也就是被持有了,也就是MyCase对象被持有了两次(一次init,一次被blk变量持有),在BlockCase对象调用release时,引用计数-1变为1。所以BlockCase对象不会调用dealloc方法,而block变量是在dealloc释放的,也就是,block不会被释放,那block持有的BlockCase对象也不会被释放。这样便造成了内存泄漏。

_Block_assign(src->forwarding, (void **)destp);这句话就是普通的赋值,里面最底层就*destptr = value;这句表达式。

那么我们在MRC下对self加入__block修饰之后呢?
typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
         __block typeof(self) blockSelf = self;
        simpleblock_ = ^{
            NSLog(@"self is %@",blockSelf);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

进行反编译,查看源代码:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __Block_byref_blockSelf_0 {
  void *__isa;
__Block_byref_blockSelf_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 typeof (self) blockSelf;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  __Block_byref_blockSelf_0 *blockSelf; // by ref
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, __Block_byref_blockSelf_0 *_blockSelf, int flags=0) : blockSelf(_blockSelf->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockCase__init_block_func_0(struct __BlockCase__init_block_impl_0 *__cself) {
  __Block_byref_blockSelf_0 *blockSelf = __cself->blockSelf; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_5642dd_mi_0,(blockSelf->__forwarding->blockSelf));
        }
static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->blockSelf, (void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __BlockCase__init_block_dispose_0(struct __BlockCase__init_block_impl_0*src) {_Block_object_dispose((void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __BlockCase__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __BlockCase__init_block_impl_0*, struct __BlockCase__init_block_impl_0*);
  void (*dispose)(struct __BlockCase__init_block_impl_0*);
} __BlockCase__init_block_desc_0_DATA = { 0, sizeof(struct __BlockCase__init_block_impl_0), __BlockCase__init_block_copy_0, __BlockCase__init_block_dispose_0};

static id _I_BlockCase_init(BlockCase * self, SEL _cmd) {
    self = ((BlockCase *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("BlockCase"))}, sel_registerName("init"));
    if (self) {
        __attribute__((__blocks__(byref))) __Block_byref_blockSelf_0 blockSelf = {(void*)0,(__Block_byref_blockSelf_0 *)&blockSelf, 33554432, sizeof(__Block_byref_blockSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self};
        (*(simpleBlock *)((char *)self + OBJC_IVAR_$_BlockCase$simpleblock_)) = ((void (*)())&__BlockCase__init_block_impl_0((void *)__BlockCase__init_block_func_0, &__BlockCase__init_block_desc_0_DATA, (__Block_byref_blockSelf_0 *)&blockSelf, 570425344));
    }
    return self;
}

int main()
{
    id myCase = ((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("BlockCase"), sel_registerName("alloc")), sel_registerName("init"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_5642dd_mi_1, myCase);
    return 0;
}

我们看到比不加修饰符,多了:

struct __Block_byref_blockSelf_0 {
  void *__isa;
__Block_byref_blockSelf_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 typeof (self) blockSelf;
};

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

上一篇block深究里面,我们已经解释过__Block_byref,这里的__Block_byref_blockSelf_0多了两个东西void (__Block_byref_id_object_copy)(void, void*);和void (__Block_byref_id_object_dispose)(void);。以及外面多了两个方法__Block_byref_id_object_copy_131和__Block_byref_id_object_dispose_131,多的这些都是干什么的呢是干什么的呢?我们一会儿就会提到。

我们继续看看辅助函数__BlockCase__init_block_copy_0

static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->blockSelf, (void*)src->blockSelf, 8/*BLOCK_FIELD_IS_BYREF*/);}

我们看到_Block_object_assign方法传入的参数,第三个类型为8/BLOCK_FIELD_IS_BYREF/,我们查看_Block_object_assign源码:

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
        if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
            _Block_assign_weak(object, destAddr);
        }
        else {
            // do *not* retain or *copy* __block variables whatever they are
            _Block_assign((void *)object, destAddr);
        }
    }
    else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
        // copying a __block reference from the stack Block to the heap
        // flags will indicate if it holds a __weak reference and needs a special isa
        _Block_byref_assign_copy(destAddr, object, flags);
    }
    // (this test must be before next one)
    else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
        // copying a Block declared variable from the stack Block to the heap
        _Block_assign(_Block_copy_internal(object, flags), destAddr);
    }
    // (this test must be after previous one)
    else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
        _Block_retain_object(object);
        _Block_assign((void *)object, destAddr);
    }
}

根据代码flags匹配到执行的片段为:

else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
        // copying a __block reference from the stack Block to the heap
        // flags will indicate if it holds a __weak reference and needs a special isa
        _Block_byref_assign_copy(destAddr, object, flags);
    }

我们发现这里没有对传入的对象做引用计数做相关操作,而是直接执行了_Block_byref_assign_copy方法,那么_Block_byref_assign_copy方法的源代码又是如何呢,我们看一下:

static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
    struct Block_byref **destp = (struct Block_byref **)dest;
    struct Block_byref *src = (struct Block_byref *)arg;

    ...
    else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
    // src points to stack
        bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
        // if its weak ask for an object (only matters under GC)
        struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
        copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
        copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
        src->forwarding = copy;  // patch stack to point to heap copy
        copy->size = src->size;
        if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
            // Trust copy helper to copy everything of interest
            // If more than one field shows up in a byref block this is wrong XXX
            copy->byref_keep = src->byref_keep;
            copy->byref_destroy = src->byref_destroy;
            (*src->byref_keep)(copy, src);
        }
        ...
    }
    ...
}

(*src->byref_keep)(copy, src);调用__Block_byref_blockSelf_0的__Block_byref_id_object_copy方法,即:

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}

131为BLOCK_FIELD_IS_OBJECT (3) |BLOCK_BYREF_CALLER(128),我们看看_Block_object_assign的实现:

void _Block_object_assign(void *destAddr, const void *object, const int flags) {
    //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
    if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
        if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
            _Block_assign_weak(object, destAddr);
        }
        else {
            // do *not* retain or *copy* __block variables whatever they are
            _Block_assign((void *)object, destAddr);
        }
    }
    ...
}

根据flags的匹配,最终的执行代码是:

else {
      // do *not* retain or *copy* __block variables whatever they are
      _Block_assign((void *)object, destAddr);
  }

我们之前说_Block_assign里面就是一个赋值,没有其他操作,所以得出结论,加入__block后,没有对self做引用计数的相关操作,也就是block不会持有MyCase对象。所以也就不会造成循环引用。

1.ARC的情况

ARC下不加修饰
typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        simpleblock_ = ^{
            NSLog(@"self is %@",self);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

使用clang进行反编译后,得到如下内容:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  BlockCase *self;
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, BlockCase *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockCase__init_block_func_0(struct __BlockCase__init_block_impl_0 *__cself) {
  BlockCase *self = __cself->self; // bound by copy
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_29fa49_mi_0,self);
        }
static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __BlockCase__init_block_dispose_0(struct __BlockCase__init_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __BlockCase__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __BlockCase__init_block_impl_0*, struct __BlockCase__init_block_impl_0*);
  void (*dispose)(struct __BlockCase__init_block_impl_0*);
} __BlockCase__init_block_desc_0_DATA = { 0, sizeof(struct __BlockCase__init_block_impl_0), __BlockCase__init_block_copy_0, __BlockCase__init_block_dispose_0};

static id _I_BlockCase_init(BlockCase * self, SEL _cmd) {
    self = ((BlockCase *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("BlockCase"))}, sel_registerName("init"));
    if (self) {
        (*(simpleBlock *)((char *)self + OBJC_IVAR_$_BlockCase$simpleblock_)) = ((void (*)())&__BlockCase__init_block_impl_0((void *)__BlockCase__init_block_func_0, &__BlockCase__init_block_desc_0_DATA, self, 570425344));
    }
    return self;
}

int main()
{
    id myCase = ((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("BlockCase"), sel_registerName("alloc")), sel_registerName("init"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_29fa49_mi_1, myCase);
    return 0;
}

这里反编译得到的代码和之前在MRC相同情况下反编译的一样,不做多余解释,这里也造成了循环引用。

那么在ARC下加入__block修饰呢?写如下代码:
typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        __block typeof(self) blockSelf = self;
        simpleblock_ = ^{
            NSLog(@"self is %@",blockSelf);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}

在ARC情况下,我们知道一般的指针变量默认就是strong类型的,因此一般我们对于strong变量不加__strong修饰,strong是在ARC后引入的关键字,strong类似于retain,引用时候会引用计数+1,以下两行代码是等价的:

  BlockCase *case1 = [[BlockCase alloc] init];
  __strong BlockCase *case1 = [[BlockCase alloc] init];

也就是说:

 __block typeof(self) blockSelf = self;

等价于

__block __strong typeof(self) blockSelf = self;

__strong为强引用,所以即使加了__block捕获的对象self仍然会被retain。所以还是会造成循环引用,产生内存泄露。

那么我们使用__weak修饰会怎么样呢?

我们使用如下代码:

typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        __weak typeof(self) weakSelf = self;
        simpleblock_ = ^{
            NSLog(@"self is %@",weakSelf);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}

进行反编译得到:


struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  __weak typeof (self) weakSelf;
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, __weak typeof (self) _weakSelf, int flags=0) : weakSelf(_weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockCase__init_block_func_0(struct __BlockCase__init_block_impl_0 *__cself) {
  __weak typeof (self) weakSelf = __cself->weakSelf; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_fbf628_mi_0,weakSelf);
        }
static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->weakSelf, (void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __BlockCase__init_block_dispose_0(struct __BlockCase__init_block_impl_0*src) {_Block_object_dispose((void*)src->weakSelf, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __BlockCase__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __BlockCase__init_block_impl_0*, struct __BlockCase__init_block_impl_0*);
  void (*dispose)(struct __BlockCase__init_block_impl_0*);
} __BlockCase__init_block_desc_0_DATA = { 0, sizeof(struct __BlockCase__init_block_impl_0), __BlockCase__init_block_copy_0, __BlockCase__init_block_dispose_0};

static id _I_BlockCase_init(BlockCase * self, SEL _cmd) {
    self = ((BlockCase *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("BlockCase"))}, sel_registerName("init"));
    if (self) {
        __attribute__((objc_gc(weak))) typeof(self) weakSelf = self;
        (*(simpleBlock *)((char *)self + OBJC_IVAR_$_BlockCase$simpleblock_)) = ((void (*)())&__BlockCase__init_block_impl_0((void *)__BlockCase__init_block_func_0, &__BlockCase__init_block_desc_0_DATA, weakSelf, 570425344));


    }
    return self;
}

int main()
{
    id myCase = ((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("BlockCase"), sel_registerName("alloc")), sel_registerName("init"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_fbf628_mi_1, myCase);
    return 0;
}

我们发现了一个地方和之前有不同:

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  __weak typeof (self) weakSelf;
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, __weak typeof (self) _weakSelf, int flags=0) : weakSelf(_weakSelf) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

这里block里面的weakSelf变量竟然使用了__weak进行修饰,也就是说是弱引用,不让改变实例的引用计数,也就是不会持有self。这样就做到消除循环引用了。

综上所述,我们知道了MRC下面消除循环引用使用__block, ARC下面使用__weak,以及为什么ARC下面不能使用__block消除循环引用的原因。

看到这里,也许你会问一个问题,在之前我们写一些代码的时候,好像没有用到self,也会产生循环引用,例如以下代码:

@interface BlockCase() {
    simpleBlock simpleblock_;
    NSString *_name;

}
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        simpleblock_ = ^{
            NSLog(@"name is %@",_name);
        };
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

我们看到block里面用到了BlockCase的一个成员变量,直接使用_name的方式使用,根据我们的经验,我们知道这样也会产生循环引用,为什么呢,看一下源代码:


struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __BlockCase__init_block_impl_0 {
  struct __block_impl impl;
  struct __BlockCase__init_block_desc_0* Desc;
  BlockCase *self;
  __BlockCase__init_block_impl_0(void *fp, struct __BlockCase__init_block_desc_0 *desc, BlockCase *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __BlockCase__init_block_func_0(struct __BlockCase__init_block_impl_0 *__cself) {
  BlockCase *self = __cself->self; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_bbfcaf_mi_0,(*(NSString **)((char *)self + OBJC_IVAR_$_BlockCase$_name)));
        }
static void __BlockCase__init_block_copy_0(struct __BlockCase__init_block_impl_0*dst, struct __BlockCase__init_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __BlockCase__init_block_dispose_0(struct __BlockCase__init_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __BlockCase__init_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __BlockCase__init_block_impl_0*, struct __BlockCase__init_block_impl_0*);
  void (*dispose)(struct __BlockCase__init_block_impl_0*);
} __BlockCase__init_block_desc_0_DATA = { 0, sizeof(struct __BlockCase__init_block_impl_0), __BlockCase__init_block_copy_0, __BlockCase__init_block_dispose_0};

static id _I_BlockCase_init(BlockCase * self, SEL _cmd) {
    self = ((BlockCase *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("BlockCase"))}, sel_registerName("init"));
    if (self) {
        (*(simpleBlock *)((char *)self + OBJC_IVAR_$_BlockCase$simpleblock_)) = ((void (*)())&__BlockCase__init_block_impl_0((void *)__BlockCase__init_block_func_0, &__BlockCase__init_block_desc_0_DATA, self, 570425344));


    }
    return self;
}

int main()
{
    id myCase = ((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)((BlockCase *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("BlockCase"), sel_registerName("alloc")), sel_registerName("init"));
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_s5_77zrry7j3b570h0tq36z405c0000gn_T_BlockCase_bbfcaf_mi_1, myCase);
    return 0;
}

在结构体__BlockCase__init_block_impl_0里面,我们发现竟然出现了 BlockCase *self; 也就是说虽然我们直接使用了_name,看似没有用到self,但是源代码里面还是引用了self,所以这样就造成了循环引用。
下面再来看看直接使用self.name呢,使用如下代码:

typedef void(^simpleBlock)();

@interface BlockCase() {
    simpleBlock simpleblock_;
    NSString *_name;

}
@property (nonatomic, strong) NSString *name;
@end

@implementation BlockCase

- (id)init
{
    self = [super init];
    if (self) {
        simpleblock_ = ^{
            NSLog(@"name is %@",self.name);
        }
    }
    return self;
}

int main()
{
    id myCase = [[BlockCase alloc] init];
    NSLog(@"%@", myCase);
    return 0;
}
@end

经过反编译,发现发编译后的代码和使用_name效果是一样的,都会引用循环引用。所以我们得出当在block里面引用了对象的成员变量的时候,也会和self造成循环引用,所以要加__weak修饰来避免循环引用,也就是:

- (id)init
{
    self = [super init];
    if (self) {
        __weak typeof(self) weakSelf = self;
        simpleblock_ = ^{
            NSLog(@"name is %@",weakSelf.name);
        };
    }
    return self;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值