我们创建一个对象如果我们block内部用到了__block类型的变量,他会拥有这个对象,我们可以通过cpp文件来分析。一旦访问对象,desc结构体里面就会多两个成员,一个是copy 和dispose,他会对对这个变量进行内存管理。block会对__block变量直接产生强力用类似于我们之前访问对象类型的变量,但是对象类型的变量block会根据我们访问对象的引用是强引用还是弱引用对他产生相应的引用。
__weak只应用到oc对象和block对象
当block在栈上时,并不会对__block变量产生强引用,ARC中一旦访问了外部的变量,会自动帮我们复制到堆上面,从而对自己内部的age变量产生强引用,因为原来的变量还是在栈上面的,用堆上面block访问栈上面的结构体变量有问题,所以他会自动将block内部使用到的__block变量内存也copy到堆上面,而且block会对这个变量产生强引用,如果还有其他block也访问,那个__block变量不会在复制了.
当block被copy到堆时
会调用block内部的copy函数
copy函数内部会调用_Block_object_assign函数
_Block_object_assign函数会对__block变量形成强引用(retain)
![](https://i-blog.csdnimg.cn/blog_migrate/93b3abb92484b2d9a2c13ca84bdcb292.png)
当block从堆中移除时
会调用block内部的dispose函数
dispose函数内部会调用_Block_object_dispose函数
_Block_object_dispose函数会自动释放引用的__block变量(release)
![](https://i-blog.csdnimg.cn/blog_migrate/1147eb3faf056c4be04ccce8aa33bc0e.png)
因为加了__block之后,他在内存中就是一个对象,所以要对他进行内存管理,因为block内部直接使用这个变量,所以要对他进行内存管理,所以有block决定什么copy和释放。
内存管理02
block内部会访问很多中局部变量
1 int 2 __block int 3 id __weak
像对象类型的auto变量,
当block拷贝到堆上时,都会通过copy函数来处理它们
__block变量(假设变量名叫做a)
_Block_object_assign((void)&dst->a, (void)src->a, 8/BLOCK_FIELD_IS_BYREF/);
对象类型的auto变量(假设变量名叫做p)
_Block_object_assign((void)&dst->p, (void)src->p, 3/BLOCK_FIELD_IS_OBJECT/);
当block从堆上移除时,都会通过dispose函数来释放它们
__block变量(假设变量名叫做a)
_Block_object_dispose((void)src->a, 8/BLOCK_FIELD_IS_BYREF*/);
对象类型的auto变量(假设变量名叫做p)
_Block_object_dispose((void)src->p, 3/BLOCK_FIELD_IS_OBJECT*/);
![](https://i-blog.csdnimg.cn/blog_migrate/ee128988a97384edefae03acaac60d7a.png)
为什么使用forwarding 来访问__block age结构体里面的age,因为假如我们这个变量在栈上面的时候,forwarding指针指向的是自己栈上面的age,但是一旦被复制到了堆上面之后,我再使用栈上面的age反问的时候,他会根据俄forwarding找到堆上面的age来访问,对上的forwarding指向堆上面age结构体自己,这样无论我们用那个age来访问,最终访问的都是堆内存上面的age变量
forwarding的改变
forwarding指针
当copy到堆上面的之后,外面的age和堆里面age的结构体里面的age的指针有没有变化
肯定是有变化的因为一个在栈一个在堆上面
内存管理03
__block修饰对象类型
__block Person *p = [[Person alloc] init];
void(^block)(void) = ^
{
NSLog(@"%@", p);
};
block();
他在内存中生成的代码和之前不同,他是多了一个结构体,然后再用结构体区访问这个变量,block会引用这个多出来的结构体,对他强引用,然后这个__Block_byref_p_0结构体根据外面传进来的是钱引用还是弱引用,堆内部的p对象产生相应的引用
struct __Block_byref_p_0 {
void *__isa;
__Block_byref_p_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
Person *p;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_p_0 *p; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_p_0 *_p, int flags=0) : p(_p->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
内存管理04(__block 对象类型)
一旦结构体被copy到堆里面之后,那个结构体成员也会调用自己内部的copy函数,他会执行assign方法。里面有个+40,相当于person那个指针,从而对其进行强或者弱引用。
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
当block从堆中移除的时候,他也会这个结构体进行一次移除操作,这个结构体也会调用自己内部的dispose,然后将+40指针进行释放
当__block变量被copy到堆时
会调用__block变量内部的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)