关于OC中block的本质结构、block的变量捕获方式请查看OC中block底层原理总结(上)
需要先看懂上篇文章再往下看
一、block内部修改捕获变量的值
不能修改的原因
对于static局部变量和全局变量,block中是可以进行修改的;但是对于auto变量,确不能直接修改。从OC中block底层原理总结(上)里面可以知道,当引用auto变量时,捕获了一个新变量在内部了,当block调用时其内部使用的实际上是自己捕获的那个新变量,修改后两个变量值不能同步,所以修改是不合法的。
使可修改的方法__block
的工作原理
对代码生成cpp文件查看代码本质结构:
__block int age = 10;
void (^block)(void) = ^{ age = 9; };
//通过命令行
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
block的构成情况如下
//block的结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
};
// __block int age 变成了一个结构体
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
// __block int age = 10; 这句代码初始化变成了__Block_byref_age_0结构体初始化,赋值情况为:
{
(void*)0, // 即 void *__isa;
(__Block_byref_age_0 *)&age, // 即 __Block_byref_age_0 *__forwarding;
0, // 即 int __flags;
sizeof(__Block_byref_age_0), // 即 int __size;
10 // 即 int age;
};
总结:
- __block声明的变量在运行时变为了一个结构体。
- 在block内部, 对变量修改即为对
__Block_byref_age_0
内部对应的成员变量修改。- 在block外部,对变量进行修改也是修改的
__Block_byref_age_0
内部对应的那个成员变量,所以就实现了auto变量在block中可修改,值也能同步。
当__block修饰的是对象类型的auto变量时
生成的结构体变为
// Person是自定义的一个类,在查看时不能用系统类所以定义
struct __Block_byref_p_1 {
void *__isa;
__Block_byref_p_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *__strong p;
};
相比修饰基本数据类型时的变化:
- 结构体中多了
__Block_byref_id_object_copy
和__Block_byref_id_object_dispose
两个函数指针 - 如果
__block
后面增加修饰符__weak
时,上面的结构体中__strong
会变为__weak
。
查看函数指针指向函数内部得出结论:
- 生成的结构体最后一个成员是
__weak
还是__strong
取决于声明时写的。 - 当block被拷贝时,会调用结构体
__Block_byref_p_1
中的copy函数,这个函数内部会决定是对对象强还是弱引用。 - 当block销毁时,会调用结构体
__Block_byref_p_1
中的dispose函数,对对象移除引用。
二、block循环引用
循环引用的形成即: 对象A(或强引用的对象)强引用了block, block中又强引用了对象A,导致引用计数器一直不为0,释放不了对象。
解决的方法:正确使用@weakify 和@strongify防止block循环引用
网友评论