iOS-从循环引用看Block

2021-03-15 10:33:56 浏览数 (1)

中介者模式 (自动)

循环引用

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 aint 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)

copycopy

}

}

_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

0 人点赞