前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS-从循环引用看Block

iOS-从循环引用看Block

原创
作者头像
Wilbur-L
修改2021-03-15 10:33:56
1.1K0
修改2021-03-15 10:33:56
举报
文章被收录于专栏:iOS底层原理iOS底层原理

中介者模式 (自动)

循环引用

self.block=^(void){

};

self.block();

self生命周期: self->block->self

weakSelf

_ _weak typeof(self)weakSelf = self;

加入一层弱引用就可以解决循环引用了吗?

如果在block里异步调用weakSelf,会导致提前释放

_ _weak typeof(self)weakSelf = self;

self.block=^(void){

dispatch();

NSlog(@"%@",weakSelf.age);

};

self.block();

self生命周期:self->block->weakSelf->block

说明:在block区内已经释放,到self.block()调用时已经被释放,所以值为null。

weakSelf不足以保证self的生命周期。

Strong

_ _weak typeof(self)weakSelf = self;

self.block=^(void){

__strong __typeof(weakSelf)strongSelf=weakSelf;

dispatch();

NSlog(@"%@",weakSelf.age);

};

self生命周期:self->block->weakSelf->Strong->weak->self

self在block区间打印完Log后才释放,self在弱引用表里面设置为nil,所以后续self才可以释放。

__block (手动)

__block ViewController *vc = self;

self.block=^void{

dispatch(){

NSLog();

vc=nil;

};

};

self.block();

self生命周期:self->vc->nil->block

原因总结:循环引用的解决方法无非就是解决self和block的通讯。

block区间
block区间

通讯的方式有很多:传参,协议,代理等等.

传参

typedef void(^Block) (ViewController *);

@property(nonatomic,copy)Block block;

-(void)viewDidload{

self.name=@"Wilbur";

self.block=^(ViewController *vc){

dispatch();

NSLog(@"%@",vc.name);

}

};

self.block();

vc作为参数压栈进block对self不造成持有关系

self生命周期:self->block->_vc

Clang层看block

int main(){

void(*block)(void)=(

&main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA)

);

__block_imp(block->FuncPtr(block());

}

static void __main_block_func_0(struct __main_block_impl_0 *__cself){

printf("hello world");

}

##impl_0 的调用func_0

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;

}

};

static struct __main_block_desc_0{

size_t reserved;

size_t Block_size;

}__ main_block_desc_0_DATA={0,sizeof(struct __main_block_impl_0)};

由此可以看出block实际上是一个匿名函数调用的__main_block_impl_0

__block捕获外界变量

如果在外界添加一个变量 int a = 10;

变化

static void __main_block_func_0(struct __main_block_impl_0 *__cself){

int a = __cself->a; //第一层copy

printf("hello world");

}

int main(){

int a = 10;

mian_block_impl_0(func_0,&DATA,a);

}

struct __main_block_impl_0{

int a;

__main_block_impl_0(*fp,*desc,int _a,inf flags):a(_a){

xxx与上诉一样

}

}

可以看到 int a = _cself->a 与 int a=10 指向了同一片内存空间

int a
int a

那么再给__block 引用一个外界变量

__block int a=10;

int main(){

__Block_byref_a_0 a ={

void* 0,

(__Block_byref_a_a *)&a,

0,

sizeof(__Block_byref_a_0),

10};

__main_block_impl_0(func_0,&DATA,&a,570425344);

}

struct __Block_byref_a_0{

void *isa;

__Block_byref_a_0 *__forwarding

int __flags;

int __size;

int a;

};

变量a被封装成了结构体对象

struct __main_block_0{

struct __block_imp impl;

struct __main_block_desc_0 *Desc;

__Block_byref_a_a *a;

__main_block_impl_0(*fp,*desc,__Block_byref_a_0 *_a,flags ):a(a->__forwarding){

impl.isa=&_NSConcreteStackBlock;

impl.Flags = flags;

impl.FuncPtr = fp;

Desc = desc;

}

};

static void __main_block_func_0(struct __main_block_impl_0 *cself){

__Block_byref_a_0 *a = __cself->a;

printf("hello world %d",(a->__forwarding->a))

}

__block a
__block a

可以看到普通变量a 和 block引用变量a的区别

1.值拷贝/指针拷贝的不同

2.生产的结构体不同,会有isa等等赋值

3.保存相应的原始变量 并赋予*a = __cself->a

4.传递一个地址给block &

Block底层

struct Block_layout{

void *isa

flags

reserve

BlockInvokeFunction invoke

Struct Block_descriptor_1 *descriptor

}

1.block的调用情况

clang层搜索private.block.h或者是bt都可以看到block的调用情况

retainBlock

NSGlobalBlock

NSStackBlock

BlockCopy

void *_Block_copy(const void *arg){

aBlock =(sturct Block_layout *)arg;

1.判断是否需要释放 retun aBlock

2.判断是否是全局block return aBlock

3.else证明这是一个栈的block,第二层复制copy

else{

sturct Block_layout *result = malloc(aBlock->descriptor->size);

memmove(result,aBlock,aBlock->descripotro->size)

#if __has_feature(ptrauch_calls)

//重新标记 invoke的指针作

result->invoke = aBlock->invoke;

#endif

//重新设置refcount

result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);

restule->flags |= BLOCK_NEEDS_FREE |2 //逻辑计数=1

_Block_call_copy_helper(result,aBlock);

//设置最新的isa

result->isa = _NSConcreteMallocBlock;

return result;

}

}

Blockdispose.cold

NSMallocBlock

Block_has_cpoy_dispose (descriptor2)

static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock){

if (!(aBlock->flags & BLOCK_HAS_COPY_DISPOSE))return NULL;

desc = aBlock->descriptor;

desc += sizeof(struct Block_descriptor_1);

return (descriptor_2 *)desc;

}

Block_has_signature(descriptor3)

static struct Block_descriptor_3 * _Block_descriptor_2(struct Block_layout *aBlock){

if (!(aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;

desc = aBlock->descriptor;

desc += sizeof(descriptor1);

if(aBlock->flags & BLOCK_HAS_COPY_DISPOSE){

desc +=size(descriptor2)

}

return (struct Block_descriptor_3 *)desc;

}

2.descriptor

descriptor1{

reserve

size

}

descriptor2{

copy

dispose

}

descriptor3{

signature

layout

}

3.block的三层拷贝

static void __main_block_func_0(sturct __main_block_impl_0 *cself){

__Block_byref_a_0 *a=__cself->a;

static void __main_block_copy_0(struct __main_block_impl_0 *dst,struct __main_block_impl_0 *src){

_Block_object_assign(&dst->a,src->a,8)

}

static void __main_block_copy_0(struct __main_block_impl_0 *dst)

{

_Block_object_dispose(src->a,8)

}

_Blcok_object_assign

void _Block_object_assign(void *destArg,const void *object ,const int flags){

**dest=destArg //二级指针拷贝

switch(os_assumes(flags&BLOCK_ALL_COPY_DISPOSE_FLAGS)){

case BLOCK_FIELE_IS_OBJECT: //判断是否是普通对象类型

_Block_retain_object(object);

*dest = object //

case BLOCK_FIELE_IS_BLOCK: //判断是否是block变量

*dest = _Block_copy(object);

case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK: //判断是否是__block修饰的变量 或者 weak修饰

case BLOCK_FIELE_IS_BYREF: //判断是否是__block修饰的变量

*dest = _Block_byref_copy(object)

copy
copy

}

}

_block_byref_copy

_block_byref_copy(*arg){

struct Block_byref *src = (struct Block_byref *) arg;三层拷贝

struct Block_byref *copy = (struct Block_byref*)malloc(src->size)

copy->isa=NULL;

copy->flags = src->flags

copy->forwarding = copy;//patch heap copy to point to itself

src->forwarding = copy;//patch stack to point to heap

从堆->栈的拷贝

copy->size = src->size

}

自从说明了__block从底层来看,为什么能够捕获外界变量的原因就在这

block三层拷贝

第一层:int a = __cself->a; //第一层值copy

第二层:指针拷贝

指针
指针

第三层:堆栈拷贝

copy->size = src->size

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 中介者模式 (自动)
    • 循环引用
      • weakSelf
        • Strong
        • __block (手动)
        • 传参
        • Clang层看block
          • __block捕获外界变量
          • Block底层
            • 1.block的调用情况
              • retainBlock
              • NSGlobalBlock
              • NSStackBlock
              • BlockCopy
              • Blockdispose.cold
              • NSMallocBlock
              • Block_has_cpoy_dispose (descriptor2)
              • Block_has_signature(descriptor3)
            • 2.descriptor
              • descriptor1{
              • }
              • descriptor2{
              • descriptor3{
            • 3.block的三层拷贝
              • _Blcok_object_assign
              • _block_byref_copy
              • _block_byref_copy(*arg){
              • }
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档