int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"123123");
};
block();
}
return 0;
}
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*block)(void) = ((void (*)())&__main_block_impl_0(
(void *)__main_block_func_0,
&__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->
FuncPtr)((__block_impl *)block);
}
return 0;
}
简化后(取出强制转化的代码)
int main(int argc, const char * argv[]) {
{ __AtAutoreleasePool __autoreleasepool;
//定义block变量: 将函数传递两个参数,然后将函数返回值的地址赋值给void (*block)(void)
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0, &__main_block_desc_0_DATA));
//执行block内部代码
block->FuncPtr)(block);
}
return 0;
}
1.2__main_block_impl_0函数内部结构,结构体名称和方法名称一样,这种写法是C++语言的语法,这种方法叫做构造函数,同时这个方法没有写返回值,其实类似于OC中的init方法,该函数的返回值就是__main_block_impl_0结构体对象本身
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//C++构造函数,返回值就是这个结构体(__main_block_impl_0)对象本身,
这里有三个参数,但外面传递进来的只有2个参数,其实这就是C++语言特性:可以设置默认值,类似于swift
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
从上面分析中我们可以知道下面的代码中,通过函数__main_block_impl_0返回的就是这个结构体(__main_block_impl_0)对象本身,然后将这个结构体的地址赋值给了block, 所以block底层本质其实就是个结构体对象
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA));
参数1: __main_block_func_0,封装了block执行逻辑的函数,简单就是将block中的任务封装到了__main_block_func_0这个函数中
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_5ad538_mi_0);
}
参数2: __main_block_desc_0_DATA, 存放block额外信息的结构体
static struct __main_block_desc_0 {
size_t reserved; //保留字段,默认是0
size_t Block_size; //结构体__main_block_impl_0所占的内存大小,其实就是block所占内存大小
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
void (^block)(void) = ^{
NSLog(@"123123");
};
//1. 底层是__main_block_impl_0结构体,里面至少保存了两个成员变量
struct __main_block_impl_0 {
struct __block_impl impl; //保存了block内代码/任务的执行
struct __main_block_desc_0* Desc; //block的描述信息
//构造函数这里先省略
};
//2. 保存了block内代码/任务的执行
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr; //指向将来执行block内函数的地址
};
//3. block的描述信息
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
}
//定义block变量: 将函数传递两个参数,然后将函数返回值的地址赋值给void (*block)(void)
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0, &__main_block_desc_0_DATA));
//执行block内部代码
block->FuncPtr)(block);
从上面我们知道block指向的是结构体__main_block_impl_0,但是__main_block_impl_0结构体中并没有FuncPtr成员变量,这里其实是做了强制类型转化,为什么可以转化?因为__main_block_impl_0结构体的地址其实也是它内部第一个成员变量的地址,所以也就是__block_impl结构体的地址,这样就可以找到block函数实现的地址FuncPtr,然后进行函数调用
int main(int argc, const char * argv[]) {
@autoreleasepool {
//auto自动变量,离开了作用域就会销毁
int age = 10;
void (^block)(void) = ^{
NSLog(@"my age is: %d",age);
};
age = 20;
block();
}
return 0;
}
打印结果: my age is: 10
int age = 10;
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0, &__main_block_desc_0_DATA, age));
age = 20;
block->FuncPtr(block);
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)
&__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_7ea761_mi_0,
age);
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//block底层结构中多了一个成员变量
int age;
//参数中age(_age)是C++语法,表示将_age的值赋值给age,即赋值给block的成员变量age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
static int height = 100;
void (^block)(void) = ^{
NSLog(@"my age is: %d, my height is: %d",age,height);
};
age = 20;
height = 200;
block();
}
return 0;
}
打印结果: my age is: 10, my height is: 200
int age = 10;
static int height = 100;
void (*block)(void) = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA, age, &height)); //将height的地址传递给这个函数
age = 20;
height = 200;
block->FuncPtr(block);
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
int *height = __cself->height; // bound by copy
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_999b2c_mi_0,
age,(*height));
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//因为block底层生成了对应的成员变量,所以block对局部变量都会捕获,无论是自动变量还是静态变量
int age;
int *height;
//age(_age):将_age的值赋值给age; height(_height):将外部传建立的_height赋值给height
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
int _age, int *_height, int flags=0) : age(_age), height(_height) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析,block捕获static静态变量时,是底层自动生成了和外部静态变量名称一样的变量指针,用来保存外部自动变量的地址,也就是block中生成的变量地址和外部静态变量的地址是一样的,所以外部改变静态变量的值,block内部的值也跟着改变,是指针捕获
思考: 为什么block捕获auto自定变量是值传递,而捕获static静态变量是指针传递? 因为对于自动变量,出了作用域就会被销毁,所以block要在访问时,先保存它的值,来避免访问已经销毁的自动变量而发生错误.而对于static变量的值,static变量即便离开了作用域,它仍然存在于内存中,直接通过指针就可以随时访问到它最新的值
int age = 10; //全局变量
static int height = 100; //全局静态变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"my age is: %d, my height is: %d",age,height);
};
age = 20;
height = 200;
block();
}
return 0;
}
打印结果: my age is: 20, my height is: 200
void (*block)(void) = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA));
age = 20;
height = 200;
block->FuncPtr(block);
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_9a7948_mi_0,
age,height); //直接访问外部的全局变量即可,不需要捕获
}
int age = 10;
static int height = 100;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//这里并没有生成对应的成员变量,所以block对全局变量(全局变量+全局静态变量)是不会捕获的
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//WGPerson.h文件
@interface WGPerson : NSObject
-(void)test;
@end
//WGPerson.m文件
@implementation WGPerson
-(void)test {
void (^block)(void) = ^{
NSLog(@"-----%@",self);
};
block();
}
@end
// test()方法转化后,可以发现调用test方法,实际传递了两个参数:
self对象本身,还有就是方法名称,函数参数也是局部变量,既然是局部变量,那么就都会被block捕获
static void _I_WGPerson_test(WGPerson * self, SEL _cmd) {
//将self作为参数再传递给block底层构造方法,
void (*block)(void) = &__WGPerson__test_block_impl_0(
__WGPerson__test_block_func_0, &__WGPerson__test_block_desc_0_DATA,
self, 570425344));
block->FuncPtr(block);
}
struct __WGPerson__test_block_impl_0 {
struct __block_impl impl;
struct __WGPerson__test_block_desc_0* Desc;
//生成对应的成员变量
WGPerson *self;
//self(_self)接收外部传递进来的_self参数,然后赋值给self
__WGPerson__test_block_impl_0(void *fp,
struct __WGPerson__test_block_desc_0 *desc,
WGPerson *_self, int flags=0) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析,调用对象的方法,底层实际是传递了两个参数: 一个是对象本身self,一个是方法SEL,既然是参数,那么就是局部变量,只要是局部变量,那么就一定会被block捕获. 如果在案例test方法的block中访问WGPerson的成员变量,那么实际block上捕获的是WGPerson对象self本身,然后通过self再去访问它的成员变量
变量类型 捕获到block内部 访问方式
局部变量 auto 捕获 值传递
static 捕获 指针传递
全局变量 全局变量 不捕获 直接访问
int main(int argc, const char * argv[]) {
@autoreleasepool {
//类型一->继承关系: __NSGlobalBlock__:__NSGlobalBlock:NSBlock:NSObject:(null)
void (^block)(void) = ^{
NSLog(@"hello world");
};
NSLog(@"%@",[block class]); //block类对象
NSLog(@"%@",[[block class] superclass]); //block类对象的父类
//block类对象的父类的父类
NSLog(@"%@",[[[block class] superclass] superclass]);
NSLog(@"%@",[[[[block class] superclass] superclass] superclass]);
NSLog(@"%@",[[[[[block class] superclass] superclass] superclass] superclass]);
}
return 0;
}
打印结果: __NSGlobalBlock__
__NSGlobalBlock
NSBlock
NSObject
(null)
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
分析,从结果打印可知,block本质就是一个OC对象,最终是继承自NSBlock类型, 基类就是NSObject, 那么就可以明白之前研究的block底层结构中的isa指针就是从NSObject中继承来的, 同理我们可以打印其他block类型
int main(int argc, const char * argv[]) {
@autoreleasepool {
//类型二->继承关系:__NSMallocBlock__:__NSMallocBlock:NSBlock:NSObject:(null)
int age = 10;
void (^block)(void) = ^{
NSLog(@"hello world---%d",age);
};
block();
}
return 0;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
//类型三->继承关系: __NSStackBlock__:__NSStackBlock:NSBlock:NSObject:(null)
int age = 10;
NSLog(@"--%@---%@---%@",[^{
NSLog(@"----%d",age);
} class],
[[^{} class] superclass],[[[^{} class] superclass] superclass]);
}
return 0;
}
打印结果: --__NSStackBlock__---__NSGlobalBlock---NSBlock
int main(int argc, const char * argv[]) {
@autoreleasepool {
//__NSGlobalBlock__
void (^block1)(void) = ^{
NSLog(@"hello world");
};
//__NSStackBlock__
int age = 10;
void (^block2)(void) = ^{
NSLog(@"hello world---%d",age);
};
//__NSStackBlock__
NSLog(@"%@ %@ %@",[block1 class],[block2 class],[^{
NSLog(@"%d",age);
} class]);
}
return 0;
}
//block1
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp,
struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block2
struct __main_block_impl_1 {
struct __block_impl impl;
struct __main_block_desc_1* Desc;
int age;
__main_block_impl_1(void *fp,
struct __main_block_desc_1 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
分析,我们可以看出编译后的block的isa都指向了**&_NSConcreteStackBlock类型,这个和我们运行打印的结果是不一样的, 为什么? 我们一切以Runtime运行时为准,我们通过clang生成的C++代码并不是我们OC真正生成的代码,会有一些差异化的改变,只是可以作为参考,因为LLVM编译器从某个版本开始,不在生成C++代码,而是生成了一种中间文件,这种中间文件和我们clang**出来的C++文件还是有所差别的,但是差别不大,(clang是属于LLVM编译器种的一部分)
应用程序的内存分配:
程序区域(.text区): 程序代码
数据区域(.data区): 全局变量/static变量
堆区: alloc/malloc出来的对象,动态分配内存,需要我们程序员自己申请和管理内存
栈区: 局部变量,函数参数等
__NSGlobalBlock__(NSConcreteGlobalBlock): 数据区
__NSMallocBlock__(_NSConcreteMallocBlock): 堆区
__NSStackBlock__(_NSConcreteStackBlock): 栈区
int height = 100;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block1)(void) = ^{
NSLog(@"hello world---");
};
NSLog(@"block1----%@",[block1 class]);
static int age = 10;
void (^block2)(void) = ^{
NSLog(@"my age is: %d",age);
};
NSLog(@"block2----%@",[block2 class]);
void (^block3)(void) = ^{
NSLog(@"my height is: %d",height);
};
NSLog(@"block3----%@",[block3 class]);
}
return 0;
}
打印结果: block1----__NSGlobalBlock__
block2----__NSGlobalBlock__
block3----__NSGlobalBlock__
分析,没有访问auto自动变量的都是NSGlobalBlock类型的, 存放在数据段中. 为什么访问了static静态变量/全局变量的block是NSGlobalBlock类型? 推测可能是因为这些变量存储在数据段中,离开作用域不会销毁的原因
int age = 10;
void (^block)(void) = ^{
NSLog(@"my age is: %d",age);
};
block();
NSLog(@"%@",[block class]);
//在MRC(手动管理内存)环境下
打印结果: my age is: 10
__NSStackBlock__
//在ARC(自动管理内存)环境下
打印结果: my age is: 10
__NSMallocBlock__
分析,访问了auto变量的block在ARC环境下就是__NSMallocBlock__类型,在MRC环境下就是__NSStackBlock__类型,为什么ARC和MRC环境下,block类型会不一致? 原因就是栈(NSStackBlock)类型block会随时销毁的,我们控制不了,在ARC自动管理内存中,ARC底层已经帮我们做了事来保证block不会被销毁,所以将栈(NSStackBlock)类型的block 变成了 堆(NSMallocBlock)类型的block,即将栈block做了一次copy变成了堆block
block类型 环境
全局block:__NSGlobalBlock__(数据区) 没有访问auto变量(不访问变量/访问static变量/访问全局变量)
栈block:__NSStackBlock__(栈区) 访问了auto变量(在Block内部使用局部变量或者OC属性,并且赋值给强引用或者Copy修饰的变量)
堆block:__NSMallocBlock__(堆区) __NSStackBlock__调用了copy(与 MallocBlock一样,可以在内部使用局部变量或者OC属性。但是不能赋值给强引用或者Copy修饰的变)
block类型 副本源的配置存储域 copy复制效果
全局block:__NSGlobalBlock 程序的数据区域 什么也不做
栈block:__NSStackBlock__(栈区) 栈 从栈复制到堆
堆block:__NSMallocBlock__(堆区) 堆 引用计数增加
- block作为函数返回值时
- 将block赋值给__strong指针时(对象默认创建的都是强指针,只是省略了__strong关键词)
- block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API的方法参数时
//WGPerson.m文件
@implementation WGPerson
-(void)dealloc {
NSLog(@"%s",__func__);
}
@end
typedef void (^WGBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
{
WGPerson *person = [[WGPerson alloc]init];
person.age = 10;
}
打断点: NSLog(@"-----");
}
return 0;
}
打印结果: -[WGPerson dealloc]
int main(int argc, const char * argv[]) {
@autoreleasepool {
WGBlock block;
{
WGPerson *person = [[WGPerson alloc]init];
person.age = 10;
block = ^{
NSLog(@"------%d",person.age);
};
}
打断点: NSLog(@"-----");
}
return 0;
}
打印结果: ------10
分析,发现WGPerson对象出了{}大括号后并没有销毁,因为没有打印信息: -[WGPerson dealloc],为什么? 因为block内部访问了person.age,即访问了person对象,那么会对person对象进行强引用, 只有block销毁了person对象才会销毁, 这里的block类型其实就是堆block,因为它被__strong指针引用着(WGBlock block;默认情况下就是__strong,只是省略了而已)
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
WGPerson *person; //这里自动生成了成员变量来保存外部的person对象的指针
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
WGPerson *_person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
上面的验证都是在ARC环境下,block类型是堆block,因为block有强指针(__strong)引用着,接下来我们看下如果在MRC环境下有什么变化, 在Build Settings中将Objective-C Automatic Reference Counting 设置为NO
int main(int argc, const char * argv[]) {
@autoreleasepool {
WGBlock block;
{
WGPerson *person = [[WGPerson alloc]init];
person.age = 10;
block = ^{
NSLog(@"------%d",person.age);
};
[person release]; //因为是MRC环境,所以person要进行一次release
}
打断点:NSLog(@"-----");
}
return 0;
}
打印结果: -[WGPerson dealloc]
分析,为什么在ARC环境下,person对象没有销毁,而在MRC环境下就销毁了? 原因是因为在MRC环境下,这里的block类型属于栈block,而栈上的block对person没有进行强引用, 如果此时对block进行一次copy操作,block类型变成堆block,那么person对象就不会销毁了,因为堆block是可以保住外部auto对象的命的,这个已经验证过了
int main(int argc, const char * argv[]) {
@autoreleasepool {
WGBlock block;
{
WGPerson *person = [[WGPerson alloc]init];
person.age = 10;
__weak WGPerson *weakPerson = person;
block = ^{
NSLog(@"------%d",weakPerson.age);
};
}
打断点:NSLog(@"-----");
}
return 0;
}
打印结果: -[WGPerson dealloc]
cannot create __weak reference because the current deployment target does
not support weak references
__attribute__((objc_ownership(weak))) WGPerson *weakPerson = person;
1 error generated.
弱引用技术是需要运行时来支持的,解决方案
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc
-fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
WGPerson *__weak weakPerson; //此时变成了弱引用
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
WGPerson *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//默认是WGPerson * person,如果clang使用了-fobjc-runtime,就会生成这种类型
WGPerson *__strong person,都是强引用,意思是一样的
WGPerson *__strong person;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
WGPerson *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
//之前访问非对象类型的auto变量时,没有下面两个方法,如果访问的是对象类型的auto变量,那么就会多出来这两个方法
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};
copy指针指向了__main_block_copy_0函数,dispose指针指向了__main_block_dispose_0函数
//当block进行copy时,就会调用下面的函数
static void __main_block_copy_0(struct __main_block_impl_0*dst,
struct __main_block_impl_0*src) {
//该函数会根据传进去的person对象是强引用还是弱引用,来对person对象进行对应的强引用或弱引用
//如果外面person对象是强引用修饰(默认就是强引用,省略了__strong),那么block就对person对象是强引用
//如果外面person对象是弱引用修饰(用__weak修饰),那么block就对person对象是弱引用
//该函数内部也会处理引用计数的问题,如果是强引,那么就会对person的引用计数+1,
在_Block_object_dispose函数中进行对应的-1操作
_Block_object_assign((void*)&dst->person, (void*)src->person,
3/*BLOCK_FIELD_IS_OBJECT*/);
}
//当block释放时,就会调用这个函数
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
//该函数会自动释放引用的auto变量,类似于release
_Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
-
如果block是在栈上,肯定不会对auto变量产生强引用(不管是在ARC还是MRC环境下)
-
如果block被拷贝copy到堆上, 会自动调用block内部的copy函数, copy函数会调用_Block_object_assign函数,_Block_object_assign函数会根据auto变量的修饰符(__storng、__weak、__unsafe_unretained)来做出相应的操作,类似retaion(形成强引用、弱引用),__storng就会强引用auto变量,__weak/__unsafe_unretained就会弱引用auto变量
-
如果block从堆上移除,会调用block内部的dispose函数,dispose函数会调用_Block_object_dispose函数,_Block_object_dispose函数会自动释放引用的auto变量,类似于release
-
为什么block底层会多出来两个函数(copy函数和dispose函数)? 因为访问的是对象类型的auto变量,而对象类型的auto变量是需要对其进行内存管理的
block内部函数 调用时机 copy函数 栈上的block复制到堆上时 dispose函数 堆上的block被废弃时
@implementation WGMainObjcVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
}
@end
打印结果: -[Person dealloc]---
-[Person dealloc]---
-[Person dealloc]---
...
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"----%@",person);
});
NSLog(@"%s",__func__);
}
打印结果: 23:18:51.727756+0800 -[WGMainObjcVC touchesBegan:withEvent:]
23:18:54.728141+0800 ----<Person: 0x600003b44670>
23:18:54.728433+0800 -[Person dealloc]---
分析,GCD中的block作为参数时,block类型是堆block,因为block内部访问了person对象,所以会对person对象进行强引用,所以直到3秒后,执行完NSLog(@"----%@",person);,block才会销毁,当block销毁的时候,会对引用的person对象进行release操作,随之person对象被销毁
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
//下面两种写法是一样的,只不过第一种写法可以省略掉类型,用typeof(person)来表示,
person是什么类型,这里就是什么类型
__weak typeof(person) weakPerson = person;
//__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"----%@",weakPerson);
});
NSLog(@"%s",__func__);
}
打印结果: 23:24:47.911207+0800 -[WGMainObjcVC touchesBegan:withEvent:]
23:24:47.911415+0800 -[Person dealloc]---
23:24:50.911296+0800 ----(null)
分析,person对象先销毁了,因为此时person对象是弱引用,所以block不会对person对象进行强引用,执行完NSLog(@"%s",func);方法后,person对象就直接销毁了,销毁后的3秒,打印了----(null)信息,说明此时访问的person对象是一个已经被销毁的对象
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"----%@",person);
});
});
NSLog(@"%s",__func__);
}
打印结果: 23:30:04.904533+0800 -[WGMainObjcVC touchesBegan:withEvent:]
23:30:07.904964+0800 ----<Person: 0x60000021c690>
23:30:07.905330+0800 -[Person dealloc]---
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"1----%@",weakPerson);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"2----%@",person);
});
});
NSLog(@"%s",__func__);
}
打印结果: 23:33:21.958500+0800 -[WGMainObjcVC touchesBegan:withEvent:]
23:33:22.958846+0800 1----<Person: 0x6000024c9830>
23:33:25.123819+0800 2----<Person: 0x6000024c9830>
23:33:25.124175+0800 -[Person dealloc]---
分析,在第一个block内,block访问的是一个弱引用类型的person对象,为什么执行完第一个block,person对象没有立即销毁哪? 因为编译器是看整体的block内有没有强引用去引用,如果有强引用,就等强引用结束后才会去释放,而不是根据弱引用来决定的
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
Person *person = [[Person alloc]init];
__weak Person *weakPerson = person;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"1----%@",person);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (2.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
NSLog(@"2----%@",weakPerson);
});
});
NSLog(@"%s",__func__);
}
打印结果: 23:39:17.005876+0800 -[WGMainObjcVC touchesBegan:withEvent:]
23:39:18.005954+0800 1----<Person: 0x600000e450d0>
23:39:18.006142+0800 -[Person dealloc]---
23:39:20.186553+0800 2----(null)
分析,因为编译器是根据block内的强引用来决定什么时候释放对象的, 所以第一个block内访问的是强引用的person对象,所以执行完第一个block代码后,person对象就销毁了,直到2秒后,执行了第2个block,此时访问的person的对象已经销毁了,所以打印的是2----(null)
typedef void (^WGBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
WGBlock block = ^ {
NSLog(@"my age is %d", age);
};
block();
}
return 0;
}
转为C++后, 可以看到这个是两个不同的函数, 在__main_block_func_0函数中是无法对main函数中的age变量进行修改值的,
__main_block_func_0函数只能修改block内部的age变量值,而修改block内部变量值并不会影响外部变量的值
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int age = 10;
WGBlock block = ((void (*)())&__main_block_impl_0(
(void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
((void (*)(__block_impl *))((__block_impl *)block)->
FuncPtr)((__block_impl *)block);
}
return 0;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_25a4d8_mi_0,
age);
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,
int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
-
将变量变为static变量(全局变量也可以,全局变量不会被block捕获,在任何地方都可以被修改),缺点就是static变量和全局变量会一直存在于内存中,不会销毁,因为这些变量是放在全局区的
typedef void (^WGBlock)(void); int main(int argc, const char * argv[]) { @autoreleasepool { static int age = 10; WGBlock block = ^ { age = 20; NSLog(@"my age is %d", age); }; block(); } return 0; } 转为C++后代码 在main函数中,将静态变量age的地址传递给了函数__main_block_impl_0 int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; static int age = 10; WGBlock block = ((void (*)())&__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA, &age)); ((void (*)(__block_impl *))((__block_impl *)block)-> FuncPtr)((__block_impl *)block); } return 0; } //在block内部会生成对应的变量指针,来保存外部传进来的变量的地址 struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; int *age; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_age, int flags=0) : age(_age) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //通过外部传递进来的变量地址对其进行修改值,虽然修改的是block内部变量的值,但是这个变量的地址 和外部变量的地址是一样的,所以外部变量就会被修改了 static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int *age = __cself->age; // bound by copy (*age) = 20; NSLog((NSString *)& __NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_c7c690_mi_0, (*age)); } -
添加__block修饰符
typedef void (^WGBlock)(void); int main(int argc, const char * argv[]) { @autoreleasepool { __block int age = 10; WGBlock block = ^ { age = 20; NSLog(@"my age is %d", age); }; block(); } return 0; }
- __block可以用于解决block内部无法修改auto变量值的问题
- __block不能修改全局变量、静态变量(static)
- 编译器会将__block修饰的变量包装成一个对象
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age =
{(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
WGBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0,
&__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->
FuncPtr)((__block_impl *)block);
//上面代码可以简化成
//__Block_byref_age_0 age = {0, &age, 0, sizeof(__Block_byref_age_0), 10};
WGBlock block = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA, &age, 570425344));
block->FuncPtr(block);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//对下面一一赋值
//__Block_byref_age_0 age = {0, &age, 0, sizeof(__Block_byref_age_0), 10};
struct __Block_byref_age_0 {
void *__isa; //这里有个isa指针,所以可以理解成一个对象
__Block_byref_age_0 *__forwarding; //指向这个结构体本身的地址
int __flags;
int __size; //结构体专用的内存大小
int age; //变量的值
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
//通过age拿到__forwarding,然后再通过__forwarding拿到age,然后对其进行修改值
(age->__forwarding->age) = 20;
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_46c985_mi_0,
(age->__forwarding->age));
}
typedef void (^WGBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
__block NSObject *obj = [[NSObject alloc]init];
WGBlock block = ^ {
obj = nil;
age = 20;
NSLog(@"my age is %d", age);
};
block();
}
return 0;
}
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __Block_byref_obj_1 {
void *__isa;
__Block_byref_obj_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj;
};
如果我们修改可变数组中的元素个数,是不需要添加__block修饰符的,因为我们只是用arr这个地址,而并不是对其进行赋值操作,只有对其进行赋值操作才需要添加__block修饰符. 能不加__block就不加,因为加__block会生成新的对象
typedef void (^WGBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *arr = [NSMutableArray array];
WGBlock block = ^ {
[arr addObject:@"123"];
};
block();
}
return 0;
}
typedef void (^WGBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
WGBlock block = ^ {
age = 20;
};
block();
NSLog(@"%p",&age);
}
return 0;
}
分析,我们现在访问age地址,其实访问的是__Block_byref_age_0结构体中变量age(int age;)的地址,而不是block底层结构体__main_block_impl_0中的成员age(__Block_byref_age_0 *age;),为什么打印的地址不是block结构体中的成员变量age? 可能是苹果想屏蔽__block内部的实现细节,就像KVO一样
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
...
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
//通过age拿到__forwarding,然后再通过__forwarding拿到age,然后对其进行修改值
(age->__forwarding->age) = 20;
NSLog((NSString *)&
__NSConstantStringImpl__var_folders_wc_tkbgc_ts0pv3lyd2n4wsdc6h0000gn_T_main_46c985_mi_0,
(age->__forwarding->age));
}
block在栈上时,__forwarding指针指向的是它自己,如果block拷贝到了堆上,那么栈上的__forwarding指针会指向堆上的block对象,堆上的__forwarding指针指向的是堆上block的自身,这样不论是访问栈上的__forwarding指针还是堆上的__forwarding指针,都可以找到堆上的变量
- 当block在栈上时,并不会对__block修饰的变量产生强引用
- 当block被拷贝到堆上时
-
1 会调用block内部的copy函数
-
2 copy函数内部会调用_Block_object_assign函数
-
3 _Block_object_assign函数会对__block变量形成强引用(retain)
typedef void (^WGBlock)(void); int main(int argc, const char * argv[]) { @autoreleasepool { __block int age = 10; WGBlock block = ^ { age = 20; }; block(); } return 0; } int main(int argc, const char * argv[]) { /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; __attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10}; WGBlock block = ((void (*)())&__main_block_impl_0( (void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, 570425344)); ((void (*)(__block_impl *))((__block_impl *)block)-> FuncPtr)((__block_impl *)block); } return 0; } struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; //block内部存在copy和dispose函数 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}; static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) { _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); } static void __main_block_dispose_0(struct __main_block_impl_0*src) { _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); }
- 当block从堆上移除时
-
1 会调用block内部的dispose函数
-
2 dispose函数内部会调用_Block_object_dispose函数
-
3 _Block_object_dispose函数会自动释放引用的__block变量(release)
typedef void (^WGBlock)(void); int main(int argc, const char * argv[]) { @autoreleasepool { NSObject *objc = [[NSObject alloc]init]; __block int age = 10; WGBlock block = ^ { NSLog(@"age: %d",age); NSLog(@"objc: %p",objc); }; block(); } return 0; } struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; NSObject *__strong objc; //强引用 __Block_byref_age_0 *age; // by ref __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *__strong _objc,__Block_byref_age_0 *_age,int flags=0) : objc(_objc), age(_age->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) { //会对__block修饰的age变量包装成的对象-强引用 _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); //会对objc变量-强引用 如果是__weak NSObject *weakSelf = objc;那么就对objc变量进行的是弱引用 _Block_object_assign((void*)&dst->objc, (void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/); }
总结: block访问auto类型的对象时,如果对象是强引用修饰(默认的都是Strong),那么block底层结构体就对外部的auto对象是强引用;如果对象是弱引用(用__weak修饰),那么block底层结构体对auto对象就是弱引用;
如果block访问的是__block修饰的对象,我们知道编译器会将__block修饰的变量包装成一个对象(底层就是个结构体,捕获到的变量是存储在这个包装的结构体中的),在block底层结构体中有个成员变量指针指向这个包装的结构体,block底层结构对这个包装的结构体是强引用,而这个包装类中捕获的变量是强引用还是弱引用就要看__block修饰的变量外部是strong修饰还是__weak修饰了;
-
当block在栈上时,对它们都不会产生强引用
-
当block拷贝到堆上时,都会通过copy函数来处理它们
__block变量age: 对age就是强引用 _Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); 对象类型的auto变量person: 若person对象是Strong修饰,block对person就是强引用; 若person对象是__weak修饰,则是弱引用 _Block_object_assign((void*)&dst->person, (void*)src->objc, 3/*BLOCK_FIELD_IS_OBJECT*/); -
当block从堆上移除时,都会通过dispose函数来释放它们
__block变量age: _Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/); 对象类型的auto变量person: _Block_object_dispose((void*)src->person, 3/*BLOCK_FIELD_IS_BYREF*/); -
__block int age = 20,不能再用__weak来修饰了,因为__weak是用来修饰对象类型的
typedef void (^WGBlock) (void);
@implementation WGMainObjcVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
__block Person *person = [[Person alloc]init];
WGBlock block = ^{
NSLog(@"---%p",person);
};
block();
}
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc
-fobjc-runtime=ios-8.0.0 WGMainObjcVC.m
转为C++代码如下
struct __Block_byref_person_0 {
void *__isa;
__Block_byref_person_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong person; //这里显示的就是强引用
};
struct __WGMainObjcVC__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __WGMainObjcVC__viewDidLoad_block_desc_0* Desc;
__Block_byref_person_0 *person; // by ref
__WGMainObjcVC__viewDidLoad_block_impl_0(void *fp,
struct __WGMainObjcVC__viewDidLoad_block_desc_0 *desc,
__Block_byref_person_0 *_person,
int flags=0) : person(_person->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
WGBlock内有个person指针,指向了__Block_byref_person_0结构体(是个强指针即强引用),__Block_byref_person_0结构体中有个person指针(Person *__strong person;),这个指针指向了我们alloc出来的person对象(强引用还是弱引用根据外部person对象是strong修饰还是__weak修饰的)
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
Person *person = [[Person alloc]init];
//若是用__weak修饰的弱引用
__block __weak Person *weakPerson = person;
WGBlock block = ^{
NSLog(@"---%p",weakPerson);
};
block();
}
struct __Block_byref_weakPerson_0 {
void *__isa;
__Block_byref_weakPerson_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__weak weakPerson; //这里显示的就是弱引用
};
struct __WGMainObjcVC__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __WGMainObjcVC__viewDidLoad_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // by ref
__WGMainObjcVC__viewDidLoad_block_impl_0(void *fp,
struct __WGMainObjcVC__viewDidLoad_block_desc_0 *desc,
__Block_byref_weakPerson_0 *_weakPerson,
int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
如果外部是弱引用,那么block中的weakPerson指针指向的是__Block_byref_weakPerson_0结构体,这里都是强引用;而__Block_byref_weakPerson_0结构体中有weakPerson指针(Person *__weak weakPerson;),它对我们alloc出来的person对象是弱引用
- 当__block变量在栈上时,不会对指向的对象产生强引用
- 当__block变量被拷贝到堆上时
- 会调用__block变量内部(包装的结构体中的dispose/copy函数,而不是block底层结构中成员变量desc中的dispose/copy函数)的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意
⚠️ 这里仅限在ARC时会retain,MRC时不会retain)
- 如果__block变量从堆上移除
- 会调用__block变量内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放指向的对象(release)
typedef void(^WGBlock) (void);
@interface Person : NSObject
//copy、strong都可以保证将block拷贝到堆上,但建议使用copy,这样无论是ARC还是MRC,这个写法都是一致的
@property(nonatomic, copy) WGBlock block;
@property(nonatomic, assign) int age;
@end
@implementation Person
-(void)dealloc {
NSLog(@"%s---",__func__);
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
Person *person = [[Person alloc]init];
person.age = 18;
person.block = ^{
NSLog(@"----%d",20);
};
NSLog(@"111111111");
}
打印结果: 111111111
-[Person dealloc]---
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
Person *person = [[Person alloc]init];
person.age = 18;
person.block = ^{
NSLog(@"age is %d",person.age);
};
NSLog(@"111111111");
}
打印结果: 111111111
struct __WGMainObjcVC__viewDidLoad_block_impl_0 {
struct __block_impl impl;
struct __WGMainObjcVC__viewDidLoad_block_desc_0* Desc;
Person *__strong person; //block底层对象中有个person指针
__WGMainObjcVC__viewDidLoad_block_impl_0(void *fp,
struct __WGMainObjcVC__viewDidLoad_block_desc_0 *desc,
Person *__strong _person, int flags=0) : person(_person) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
2.block(person):block内访问了person对象,这个person对象指向了1
|
|
person -----------> 1.MJPerson(内有_block成员):_block指向了2
-
__weak修饰(弱引用): 指向的对象销毁时,会自动让指针置为nil
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; Person *person = [[Person alloc]init]; person.age = 18; //下面两种方法都可以 __weak typeof(person) weakPerson = person; //__weak Person *weakPerson = person; person.block = ^{ NSLog(@"age is %d",weakPerson.age); }; NSLog(@"111111111"); } 打印结果: 111111111 -[Person dealloc]--- struct __WGMainObjcVC__viewDidLoad_block_impl_0 { struct __block_impl impl; struct __WGMainObjcVC__viewDidLoad_block_desc_0* Desc; Person *__weak weakPerson; //弱引用来打破循环引用 __WGMainObjcVC__viewDidLoad_block_impl_0(void *fp, struct __WGMainObjcVC__viewDidLoad_block_desc_0 *desc, Person *__weak _weakPerson, int flags=0) : weakPerson(_weakPerson) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
-
__unsafe_unretained修饰(弱引用): 指向的对象销毁时,指针存储的地址值不变
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; Person *person = [[Person alloc]init]; person.age = 18; __unsafe_unretained typeof(person) weakPerson = person; person.block = ^{ NSLog(@"age is %d",weakPerson.age); }; NSLog(@"111111111"); } 打印结果: 111111111 -[Person dealloc]--- -
__block(必须调用block并在block内对访问的变量在不需要时置为nil)
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; __block Person *person = [[Person alloc]init]; person.age = 18; person.block = ^{ NSLog(@"age is %d",person.age); person = nil; }; person.block(); NSLog(@"111111111"); } 打印结果: age is 18 -[Person dealloc]--- 111111111 //__block包装成的对象 struct __Block_byref_person_0 { void *__isa; __Block_byref_person_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); Person *__strong person; }; //block对象 struct __WGMainObjcVC__viewDidLoad_block_impl_0 { struct __block_impl impl; struct __WGMainObjcVC__viewDidLoad_block_desc_0* Desc; __Block_byref_person_0 *person; // by ref __WGMainObjcVC__viewDidLoad_block_impl_0(void *fp, struct __WGMainObjcVC__viewDidLoad_block_desc_0 *desc, __Block_byref_person_0 *_person, int flags=0) : person(_person->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
这里有三个对象,Person对象、block对象、__block包装成的对象, 首先我们知道person对象会对我们的__block包装成的对象形成强引用,而Person对象持有block对象,block对象内又持有__block包装成的对象,__block包装成的对象内部又持有并且是强引用了person对象,导致三个对象之间形成了闭环的循环引用,而现在我们主动将person对象置为nil,其实就是将__block包装成的对象内部的person指针置为nil,这样就打破了闭环的循环引用
在Xcode中设置为MRC环境(Build Setting ->automatic Reference Counting -> NO),需要注意的是在MRC环境是,是没有__weak这种弱引用的概念的,即MRC环境下不支持__weak.
-
__unsafe_unretained
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; __unsafe_unretained Person *person = [[Person alloc]init]; person.age = 18; person.block = [^{ //如果不加__unsafe_unretained,在block内会对person进行一次retain NSLog(@"age is %d",person.age); } copy]; [person release]; NSLog(@"111111111"); } 打印结果: -[Person dealloc]--- 111111111 -
__block
- (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; __block Person *person = [[Person alloc]init]; person.age = 18; person.block = [^{ NSLog(@"age is %d",person.age); } copy]; [person release]; NSLog(@"111111111"); } 打印结果: -[Person dealloc]--- 111111111