对于block,在日常开发中经常会用到,但有时候会出现一些奇奇怪怪的问题,比如block的里面值没更新上,又比如block里面要改变一个外部变量的值,就得加上__block,不然编译器就会报错。

那么,这次就来一次过搞清楚block背后不为人知的真相。

Block的基本结构


void blockTest()
{void (^block)(void) = ^{NSLog(@"Hello World!");};block();
}int main(int argc, char * argv[]) {@autoreleasepool {blockTest();}
}

通过Clang在终端输入clang -rewrite-objc main.m,会生成一个cpp文件,打开可得:

struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_0048d2_mi_0);}static struct __blockTest_block_desc_0 {size_t reserved;size_t Block_size;
} __blockTest_block_desc_0_DATA = { 0, sizeof(struct __blockTest_block_impl_0)};void blockTest()
{void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA));((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}int main(int argc, char * argv[]) {/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; blockTest();}
}static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

其实block的背后会生成一个结构体,这个结构体就是__blockTest_block_impl_0。

__blockTest_block_impl_0

struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

__blockTest_block_impl_0是Block的C++实现,是一个结构体。包含了:
impl、__blockTest_block_desc_0* Desc和一个构造函数。

__block_impl

struct __block_impl {void *isa;int Flags;int Reserved;void *FuncPtr;
};
  • 这里面有一个isa指针,指向一个类对象,有三种类型:_NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock。
  • Flags:block 的负载信息(引用计数和类型信息),按位存储。
  • *FuncPtr:一个指针,指向Block执行时调用的函数,也就是Block需要执行的代码块。在本例中是__blockTest_block_func_0函数。

__blockTest_block_desc_0

static struct __blockTest_block_desc_0 {size_t reserved;size_t Block_size;
} __blockTest_block_desc_0_DATA = { 0, sizeof(struct __blockTest_block_impl_0)};

__blockTest_block_desc_0包含了两个变量:

  • reserved:Block版本升级所需的预留区空间,在这里为0。
  • Block_size:Block大小(sizeof(struct __blockTest_block_impl_0))。

可见,block也是会占用一定的内存大小的。

__blockTest_block_func_0

static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_0048d2_mi_0);}

__blockTest_block_func_0就是Block的执行时调用的函数,参数是一个__blockTest_block_impl_0类型的指针。

blockTest

void blockTest()
{void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA));((void (*)(__block_impl *)) ((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

第一句就是定义一个block指针,指向一个通过__blockTest_block_impl_0构造函数构造出的实例对象。

第二句就是调用block,通过block->FuncPtr指针找到__blockTest_block_func_0,并转成((void (*)(__block_impl *))类型,最后将block作为参数传给函数调用。

Flags

在__block_impl中我们看到Flags。
在这里Block_private.h中可以看到:

// Values for Block_layout->flags to describe block objects
enum {BLOCK_DEALLOCATING =      (0x0001),  // runtimeBLOCK_REFCOUNT_MASK =     (0xfffe),  // runtimeBLOCK_NEEDS_FREE =        (1 << 24), // runtimeBLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compilerBLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ codeBLOCK_IS_GC =             (1 << 27), // runtimeBLOCK_IS_GLOBAL =         (1 << 28), // compilerBLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATUREBLOCK_HAS_SIGNATURE  =    (1 << 30), // compilerBLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

也就是说,一般情况下,一个 block 的 flags 成员默认设置为 0。如果当 block 需要 Block_copy() 和 Block_release 这类拷贝辅助函数,则会设置成 1 << 25 ,也就是 BLOCK_HAS_COPY_DISPOSE 类型。可以搜索到大量讲述 Block_copy 方法的博文,其中涉及到了 BLOCK_HAS_COPY_DISPOSE 。
总结一下枚举类的用法,前 16 位即起到标记作用,又可记录引用计数

BLOCK_DEALLOCATING:释放标记。一般常用 BLOCK_NEEDS_FREE 做 位与 操作,一同传入 Flags ,告知该 block 可释放。
BLOCK_REFCOUNT_MASK:一般参与判断引用计数,是一个可选用参数。
BLOCK_NEEDS_FREE:通过设置该枚举位,来告知该 block 可释放。意在说明 block 是 heap block ,即我们常说的 _NSConcreteMallocBlock 。
BLOCK_HAS_COPY_DISPOSE:是否拥有拷贝辅助函数(a copy helper function)。
BLOCK_HAS_CTOR:是否拥有 block 析构函数(dispose function)。
BLOCK_IS_GC:是否启用 GC 机制(Garbage Collection)。
BLOCK_HAS_SIGNATURE:与 BLOCK_USE_STRET 相对,判断是否当前 block 拥有一个签名。用于 runtime 时动态调用。

block截获变量

void blockTest()
{int num = 10;void (^block)(void) = ^{NSLog(@"%d",num);};num = 20;block();
}int main(int argc, char * argv[]) {@autoreleasepool {blockTest();}
}

打印出来的结果是10,我们来看看c++代码:

struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;int num;__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0) : num(_num) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {int num = __cself->num; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_3c2714_mi_0,num);
}void blockTest()
{int num = 10;void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, num));num = 20;((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

对比一开始的代码,这块代码多了个int num;
而构造函数里也多了个参数,__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int _num, int flags=0),所以在调用构造函数的时候,只是想num的值传了过去,这就能解释为什么这里打印出来的是10,而不是20。

使用static修饰变量

void blockTest()
{static int num = 10;void (^block)(void) = ^{NSLog(@"%d",num);num = 30;};num = 20;block();NSLog(@"%d",num);
}

打印出来的结果分别是20、30。
C++代码:

struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;int *num;__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int *_num, int flags=0) : num(_num) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {int *num = __cself->num; // bound by copyNSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_0,(*num));(*num) = 30;}void blockTest()
{static int num = 10;void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, &num));num = 20;NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_5a95f6_mi_1,num);((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}

这次和上次又有点不一样,不是int num而是int *num,这里传进来的是num的指针,所以,共用的是同一个变量,所以在block改变了值,在block外面也会发现值改变了。

为什么这里就用指针,而刚刚那里不用指针呢?
因为num是栈上的变量,如果用指针去访问,一旦num的作用域结束了,num被释放了这时候block再通过指针去访问变量就会有问题,就是个野指针。而对于static修饰的就不一样了,不用担心会被释放掉。

全局变量

int num = 10;void blockTest()
{void (^block)(void) = ^{NSLog(@"%d",num);num = 30;};num = 20;block();NSLog(@"%d",num);
}

打印出来的结果也是20、30.

int num = 10;struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, int flags=0) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_1875c6_mi_0,num);num = 30;}

在这里,构造函数并没有把num作为参数传进去,直接使用全局的num。

使用__block修饰变量

void blockTest()
{__block int num = 10;void (^block)(void) = ^{NSLog(@"%d",num);num = 30;};num = 20;block();NSLog(@"%d",num);
}

clang改写后的代码如下:

struct __Block_byref_num_0 {void *__isa;
__Block_byref_num_0 *__forwarding;int __flags;int __size;int num;
};struct __blockTest_block_impl_0 {struct __block_impl impl;struct __blockTest_block_desc_0* Desc;__Block_byref_num_0 *num; // by ref__blockTest_block_impl_0(void *fp, struct __blockTest_block_desc_0 *desc, __Block_byref_num_0 *_num, int flags=0) : num(_num->__forwarding) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void __blockTest_block_func_0(struct __blockTest_block_impl_0 *__cself) {__Block_byref_num_0 *num = __cself->num; // bound by refNSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_0,(num->__forwarding->num));(num->__forwarding->num) = 30;}static void __blockTest_block_copy_0(struct __blockTest_block_impl_0*dst, struct __blockTest_block_impl_0*src) {_Block_object_assign((void*)&dst->num, (void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}static void __blockTest_block_dispose_0(struct __blockTest_block_impl_0*src) {_Block_object_dispose((void*)src->num, 8/*BLOCK_FIELD_IS_BYREF*/);}static struct __blockTest_block_desc_0 {size_t reserved;size_t Block_size;void (*copy)(struct __blockTest_block_impl_0*, struct __blockTest_block_impl_0*);void (*dispose)(struct __blockTest_block_impl_0*);
} __blockTest_block_desc_0_DATA = { 0, sizeof(struct __blockTest_block_impl_0), __blockTest_block_copy_0, __blockTest_block_dispose_0};void blockTest()
{__attribute__((__blocks__(byref))) __Block_byref_num_0 num = {(void*)0,(__Block_byref_num_0 *)&num, 0, sizeof(__Block_byref_num_0), 10};void (*block)(void) = ((void (*)())&__blockTest_block_impl_0((void *)__blockTest_block_func_0, &__blockTest_block_desc_0_DATA, (__Block_byref_num_0 *)&num, 570425344));(num.__forwarding->num) = 20;((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);NSLog((NSString *)&__NSConstantStringImpl__var_folders_04_xwbq8q6n0p1dmhhd6y51_vbc0000gp_T_main_018b76_mi_1,(num.__forwarding->num));
}

我们可以先从blockTest开始看:

__Block_byref_num_0

__attribute__((__blocks__(byref))) __Block_byref_num_0 num = {(void*)0,(__Block_byref_num_0 *)&num, 0, sizeof(__Block_byref_num_0), 10};

这里定义了一个__Block_byref_num_0的实例,再看看这个实例有什么:

struct __Block_byref_num_0 {void *__isa;
__Block_byref_num_0 *__forwarding;int __flags;int __size;int num;
};

有一个isa指针,有一个指向和自己类型一样的__forwarding指针,还有一个flags、size,最后,会有一个num,num估计就是保存着变量的值。至于__forwarding指针,后面会讲到。

__blockTest_block_copy_0和__blockTest_block_dispose_0

__blockTest_block_copy_0中调用的是_Block_object_assign,__blockTest_block_dispose_0中调用的是_Block_object_dispose。

调用时机:
__blockTest_block_copy_0: __block变量结构体实例从栈拷贝到堆时
__blockTest_block_dispose_0:__block变量结构体实例引用计数为0时

BLOCK_FIELD_IS_BYREF

// Runtime support functions used by compiler when generating copy/dispose helpers// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {// see function implementation for a more complete description of these fields and combinationsBLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...BLOCK_FIELD_IS_BLOCK    =  7,  // a block variableBLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variableBLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpersBLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
};

BLOCK_FIELD_IS_OBJECT:OC对象类型
BLOCK_FIELD_IS_BLOCK:是一个block
BLOCK_FIELD_IS_BYREF:在栈上被__block修饰的变量
BLOCK_FIELD_IS_WEAK:被__weak修饰的变量,只在Block_byref管理内部对象内存时使用
BLOCK_BYREF_CALLER:处理Block_byref内部对象内存的时候会加的一个额外标记(告诉内部实现不要进行retain或者copy)

Block的内存管理

其实Block总共有三种类型:
分为全局 Block(_NSConcreteGlobalBlock)、栈 Block(_NSConcreteStackBlock)、堆 Block(_NSConcreteMallocBlock)三种形式
在ARC环境下,有哪些情况编译器会自动将栈上的把Block从栈上复制到堆上呢?

  • 调用Block的copy实例方法时
  • Block作为函数返回值返回时
  • 在带有usingBlock的Cocoa方法或者GCD的API中传递Block时候
  • 将block赋给带有__strong修饰符的id类型或者Block类型时

以上四种情况会把Block从栈复制到堆上。

当Block复制到堆上,__block修饰的变量也会跟着变化:
1、当Block在栈上的时候,__block变量也会在栈上,被栈上的Block持有着。
2、当Block被复制到堆上时,会通过调用Block内部的copy函数,copy函数内部会调用_Block_object_assign函数。此时__block变量的存储域是堆,__block变量被堆上的Block持有。
3、当堆上的Block被释放,会调用Block内部的dispose,dispose函数内部会调用_Block_object_dispose,堆上的__block被释放。

1、当多个栈上的Block使用栈上的__block变量,__block变量被栈上的多个Block持有。
2、当Block0被复制到堆上时,__block也会被复制到堆上,被堆上Block0持有。Block1仍然持有栈上的__block,原栈上__block变量的__forwarding指向拷贝到堆上之后的__block变量。
3、当Block1也被复制到堆上时,堆上的__block被堆上的Block0和Block1只有,并且__block的引用计数+1。
4、当堆上的Block都被释放,__block变量结构体实例引用计数为0,调用_Block_object_dispose,堆上的__block被释放。


下面来描述__forwarding指针的变化:

一开始,在栈上的forwarding指针是指向自身,当复制到堆以后,forwarding指针指向堆上的__Block_byref_num_0。

forwarding存在意义就是:
__forwarding 保证在栈上或者堆上都能正确访问对应变量

接下来,举个例子

int main(int argc, char * argv[]) {int num = 10;NSLog(@"%@",[^{NSLog(@"%d",num);} class]);void (^block)(void) = ^{NSLog(@"%d",num);};NSLog(@"%@",[block class]);
}

打印的结果分别是:
NSStackBlockNSMallocBlock
因为第一个block没有copy,所以只是栈上面的block,而第二个block是进行了copy,所以是堆上的block。

block截获对象

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end@implementation Person- (void)dealloc {NSLog(@"-------dealloc-------");
}@endtypedef void(^Block)(void);int main(int argc, char * argv[]) {{Person *person = [[Person alloc] init];person.name = @"roy";NSLog(@"%@",[^{NSLog(@"%@",person.name);} class]);NSLog(@"%@",@"+++++++++++++");}NSLog(@"%@",@"------------");
}

打印结果:

2022-01-04 22:50:00.490032+0800 HelloWorld[11404:3495395] __NSStackBlock__
2022-01-04 22:50:00.490791+0800 HelloWorld[11404:3495395] +++++++++++++
2022-01-04 22:50:00.490904+0800 HelloWorld[11404:3495395] -------dealloc-------
2022-01-04 22:50:00.491035+0800 HelloWorld[11404:3495395] ------------

我们看到栈上的block内部访问了局部变量,是不会对局部变量强引用的,局部变量还是能释放掉。

strong对象

@interface Person : NSObject
@property (nonatomic, strong) NSString *name;
@end@implementation Person- (void)dealloc {NSLog(@"-------dealloc-------");
}@endtypedef void(^Block)(void);int main(int argc, char * argv[]) {Block block;{Person *person = [[Person alloc] init];person.name = @"roy";block = ^{NSLog(@"%@",person.name);};person.name = @"david";NSLog(@"%@",@"+++++++++++++");}NSLog(@"%@",@"------------");NSLog(@"%@",[block class]);block ();block = NULL;
}

打印结果:

2022-01-04 22:59:32.072849+0800 HelloWorld[11771:3503869] +++++++++++++
2022-01-04 22:59:32.076322+0800 HelloWorld[11771:3503869] ------------
2022-01-04 22:59:32.076494+0800 HelloWorld[11771:3503869] __NSMallocBlock__
2022-01-04 22:59:32.076629+0800 HelloWorld[11771:3503869] david
2022-01-04 22:59:32.077177+0800 HelloWorld[11771:3503869] -------dealloc-------

Clang改写代码:

struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;Person *__strong person;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__strong _person, int flags=0) : person(_person) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

__main_block_impl_0里面多了个Person *__strong person。
这里Block截获了局部变量,当Block被拷贝到堆上的时候,使用了强引用指向这个person对象,这就是为什么只有block=NULL的时候(block会被释放),才会打印dealloc方法。

weak对象


typedef void(^Block)(void);int main(int argc, char * argv[]) {Block block;{Person *person = [[Person alloc] init];person.name = @"roy";__weak Person *weakPerson = person;block = ^{NSLog(@"%@",weakPerson.name);};weakPerson.name = @"david";NSLog(@"%@",@"+++++++++++++");}NSLog(@"%@",@"------------");block ();
}

打印结果:

2022-01-04 23:05:53.482355+0800 HelloWorld[11992:3508889] +++++++++++++
2022-01-04 23:05:53.482986+0800 HelloWorld[11992:3508889] -------dealloc-------
2022-01-04 23:05:53.483113+0800 HelloWorld[11992:3508889] ------------
2022-01-04 23:05:53.483217+0800 HelloWorld[11992:3508889] (null)

Clang改写以后的代码:


struct __main_block_impl_0 {struct __block_impl impl;struct __main_block_desc_0* Desc;Person *__weak weakPerson;__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};

在这里,__main_block_impl_0中多了个Person *__weak weakPerson成员变量。

总结:

  • 当Block内部访问了对象类型的局部变量对象时,如果Block是在栈上,将不会对auto对象产生强引用。
  • 如果block被拷贝到堆上,会调用Block内部的copy函数,copy函数内部会调用_Block_object_assign函数,_Block_object_assign会根据auto对象的修饰符(__strong,__weak,__unsafe_unretained)做出相应的操作,当使用的是__strong时,将会对person对象的引用计数加1,当为__weak时,引用计数不变。
  • 如果Block从对上移除,会调用block内部的dispose函数,内部会调用_Block_object_dispose函数,这个函数会自动释放引用的局部变量对象。

block循环引用


@interface Person : NSObject@property (nonatomic, strong) NSString *name;
@property (nonatomic, copy) void (^block)(void);- (void)testReferenceSelf;@end@implementation Person- (void)testReferenceSelf {self.block = ^ {NSLog(@"self.name = %s", self.name.UTF8String);};self.block();
}- (void)dealloc {NSLog(@"-------dealloc-------");
}@endint main(int argc, char * argv[]) {Person *person = [[Person alloc] init];person.name = @"roy";[person testReferenceSelf];
}

这是典型的循环引用,person的dealloc方法没有执行。
clang改写后的代码如下:


struct __Person__testReferenceSelf_block_impl_0 {struct __block_impl impl;struct __Person__testReferenceSelf_block_desc_0* Desc;Person *const __strong self;__Person__testReferenceSelf_block_impl_0(void *fp, struct __Person__testReferenceSelf_block_desc_0 *desc, Person *const __strong _self, int flags=0) : self(_self) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void _I_Person_testReferenceSelf(Person * self, SEL _cmd) {((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlock:"), ((void (*)())&__Person__testReferenceSelf_block_impl_0((void *)__Person__testReferenceSelf_block_func_0, &__Person__testReferenceSelf_block_desc_0_DATA, self, 570425344)));((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block"))();
}

这里,__Person__testReferenceSelf_block_impl_0有一个Person *const __strong self;
在person中block强引用了self,而self又强引用了block,导致了循环引用。

解除循环引用

@implementation Person- (void)testReferenceSelf {__weak typeof(self) weakself = self;self.block = ^ {__strong typeof(self) strongself = weakself;NSLog(@"self.name = %s", strongself.name.UTF8String);};self.block();
}- (void)dealloc {NSLog(@"-------dealloc-------");
}@end

打印结果里面会打印出dealloc,person对象被正常释放了。
clang改写后的代码如下:

struct __Person__testReferenceSelf_block_impl_0 {struct __block_impl impl;struct __Person__testReferenceSelf_block_desc_0* Desc;Person *const __weak weakself;__Person__testReferenceSelf_block_impl_0(void *fp, struct __Person__testReferenceSelf_block_desc_0 *desc, Person *const __weak _weakself, int flags=0) : weakself(_weakself) {impl.isa = &_NSConcreteStackBlock;impl.Flags = flags;impl.FuncPtr = fp;Desc = desc;}
};static void _I_Person_testReferenceSelf(Person * self, SEL _cmd) {__attribute__((objc_ownership(weak))) typeof(self) weakself = self;((void (*)(id, SEL, void (*)()))(void *)objc_msgSend)((id)self, sel_registerName("setBlock:"), ((void (*)())&__Person__testReferenceSelf_block_impl_0((void *)__Person__testReferenceSelf_block_func_0, &__Person__testReferenceSelf_block_desc_0_DATA, weakself, 570425344)));((void (*(*)(id, SEL))())(void *)objc_msgSend)((id)self, sel_registerName("block"))();
}

此时,block中对self只是个弱引用。
在Person对象的Dealloc方法中会调用weak引用的处理方法,从weak_table中寻找弱引用的依赖对象,进行清除处理。

OC底层原理-Block相关推荐

  1. OC底层原理之Runtime

    本系列主要是看完MJ的OC底层原理课程所写的总结,希望自己能加深对iOS底层的了解. OC的信息机制 OC中的方法调用其实都是转成objc_msgSend函数的调用,给receiver(方法调用者)发 ...

  2. iOS进阶之底层原理-block本质、block的签名、__block、如何避免循环引用

    面试的时候,经常会问到block,学完本篇文章,搞通底层block的实现,那么都不是问题了. block的源码是在libclosure中. 我们带着问题来解析源码: blcok的本质是什么 block ...

  3. OC底层原理-alloc流程

    alloc流程分析 可以通过opensource下载objc4来查看alloc的源码,这里用objc4-818版本来分析 1.alloc + (id)alloc {return _objc_rootA ...

  4. oc 协议 回调 静态成员_OC底层原理探究:Category、关联对象和block本质

    1.分类Category的使用 // 给MJPerson类添加分类 @interface MJPerson : NSObject - (void)run; @end@implementation MJ ...

  5. oc中block的本质及底层原理

    block的本质 block的种类及储存区域 __block的本质 block的循环引用 前言: 这里就不讨论block的具体写法及使用场景了,因为当你有一天想深入了解block 的底层原理时,你早已 ...

  6. iOS底层原理总结 - OC对象的本质

    苹果官方文档 The Objective-C language defers as many decisions as it can from compile time and link time t ...

  7. 视频教程-iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-iOS

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化 小码哥教育CEO,曾开发了2个iOS的流行开源框架(MJRefresh.MJExtension),目前在国内的使用率非常高. 李 ...

  8. iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-李明杰-专题视频课程...

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-236人已学习 课程介绍         得遇名师,突飞猛进!iOS培训王者MJ(李明杰)老师精心研发,iOS进阶课程,实用技术 ...

  9. iOS底层原理之内存管理

    文章目录 定时器 CADisplayLink.NSTimer GCD定时器 内存管理 iOS程序的内存布局 Tagged Pointer OC对象的内存管理 拷贝 引用计数的存储 dealloc 自动 ...

最新文章

  1. linux ls 命令 路径,使用ls命令在Linux中使用完整路径列出文件
  2. LSM 优化系列(二)-- dCompaction: Speeding up Compaction of the LSM-Tree via Delayed Compaction
  3. mysql行格式化_MySQL数据格式化语句
  4. 分享8个非常实用但是名气不大的软件
  5. 如何使用Openfiler为VMware ESX设置一个免费的iSCSI或NAS储存系统
  6. using bgp data to find spammers
  7. 使用Spring-AOP
  8. wmic windows
  9. r矢量球坐标系旋度_矢量与场论 | 场论
  10. 云主机开放端口笔记-mysql远程连接需要的步骤
  11. Ubuntu, python, CUDA, cuDNN, 驱动, GCC ....的对应关系
  12. Python 技巧(三)—— list 删除一个元素的三种做法
  13. 猿创征文|C++软件开发值得推荐的十大高效软件分析工具
  14. 【sketchup 2021】草图大师的高级工具使用3【复杂贴图制作实例(山体和球面贴图、全景天空绘制、吊顶添加光带)、图层(标记)工具使用、视图与样式工具的常规使用与高级使用说明】
  15. 【Sniffer和网络执法官软件助你维护网络(转网络安全)】
  16. 鸿蒙系统pc版 硬件要求,原神全平台配置要求 原神配置需求一览
  17. Ubuntu20.10系统FreeCAD 0.19编译安装
  18. 《Windows驱动开发技术详解》学习笔记
  19. matplotlib之pyplot模块——填充多边形(fill)
  20. MySQL数据库 锁

热门文章

  1. 理特报告概述了按需移动的机遇和挑战
  2. 你尝过被人误会的委屈吗?看完本篇…
  3. 算法题:解密(楚楚街2016招聘笔试)
  4. 芯片制造业变天:高通沦落 Nvidia博通崛起
  5. 计算机组成原理6 计算机得运算方法
  6. java awv音频播放界面_HTML audio 播放Base64音频流
  7. JS中的数组和字符串
  8. “下班了,我溜了”,上海一女子试用期第3天被公司辞退
  9. 论文阅读:Semantic Mapping for Mobile Robots in Indoor Scenes: A Survey
  10. fastjson依赖