__weak 成员变量

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

回顾上一篇 我们了解到了用__block修饰的变量,可以在block内部修改,__block变量其实对应一个结构体struct__Block_byref__para1_0 {void*__isa;

__Block_byref__para1_0*__forwarding;int__flags;int__size;char*_para1;

};

block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了

struct__main1_block_impl_0 {struct__block_impl impl;struct__main1_block_desc_0*Desc;

__Block_byref__para1_0*_para1;//by ref}

上上上一篇 介绍了block内部 成员变量 使用,可以在block内部修改,对应一个、多个成员变量参数,block结构体内部有一个成员引用struct__KDBlockTest__test3_block_impl_0 {struct__block_impl impl;struct__KDBlockTest__test3_block_desc_0*Desc;KDBlockTest*self;

}

这样会增加对self的引用,在Block销毁的时候引用就释放掉了

循环引用在使用的时候需要注意循环引用,即某个对象有一个copy或者strong的 block成员属性,这时block内部直接引用了 成员变量(全局变量) 或者self,这样就产生了self持有 block成员,block成员又持有了self,就会导致循环引用。

我们看以下代码(ARC):

typedefvoid(^ActionTest)(void);@interfaceKDBlockTest()

{

__block NSString*_person2;

ActionTest _action;

}

@implementationKDBlockTest#PRagmamark - system

-(instancetype)init

{

self=[super init];if(self)

{

[self test3];

}returnself;

}-(void)dealloc

{

NSLog(@"KDBlockTest dealloc");

}#pragmamark - private////循环引用-(void)test3

{

_person2=@"person2";

_action= ^(void) {//block内赋值NSLog(@"excuteing _person2:%@,%p",_person2,_person2);

};

_action();

}

这样我们执行以下代码:

KDBlockTest *_test=[[KDBlockTest alloc]init];

通过调试发现没有走到dealloc,这里不管成员变量 _person2 是否声明 __block都没有什么影响。

成员变量 这一篇已经详细介绍了,对于block 使用 成员变量、self来说,block内部是直接强引用self的。也就是block持有了self,在这里bock又作为self的一个成员被持有,所以就形成了相互引用,导致无法释放。

__weak对于上面这种情况,我们引入了__weak解决,__weak不会增加对象的引用计数,而且当指向的内存销毁后,__weak指针会自动置为nil。

我们对上面的代码稍作修改

-(void)test3

{__weaktypeof(self) _weakSelf=self;_person2=@"person2";

NSLog(@"init:%@,%p,%p",_person2,_person2,&self);

_action= ^(void) {//block内赋值NSLog(@"excuteing _person2:%@,%p,%p",_weakSelf.person2,_weakSelf.person2,&_weakSelf);

};

_action();

}

输出日志:

2014-07-2913:38:30.872Test[2642:60b] init:person2,0x5b980,0x27dae9442014-07-2913:38:30.875Test[2642:60b] excuteing _person2:person2,0x5b980,0x1562ed442014-07-2913:38:30.876Test[2642:60b] KDBlockTest dealloc

从日志可以看出block内部使用 person2 、_weakSelf 和外面的 person2 、self 地址是一样的,看来也是引用关系,既达到block内部修改变量的效果,又没有对变量产生强引用。我们来看下转换后的代码:

block结构体的定义:

struct__KDBlockTest__test3_block_impl_0 {struct__block_impl impl;struct__KDBlockTest__test3_block_desc_0*Desc;__weaktypeof(self) _weakSelf;__KDBlockTest__test3_block_impl_0(void*fp,struct__KDBlockTest__test3_block_desc_0 *desc, __weaktypeof(self) __weakSelf,intflags=0) : _weakSelf(__weakSelf) {

impl.isa= &_NSConcreteStackBlock;

impl.Flags=flags;

impl.FuncPtr=fp;

Desc=desc;

}

};

重点就在这,使用_weak声明的self,block结构体对应 也生成了一个_weak的self成员。我们在看下 我们的test3 方法:

staticvoid_I_KDBlockTest_test3(KDBlockTest *self, SEL _cmd) {__attribute__((objc_gc(weak)))typeof(self) _weakSelf=self;(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_1;

NSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_2,(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),&self);

(*(ActionTest *)((char*)self + OBJC_IVAR_$_KDBlockTest$_action))= (void(*)())&__KDBlockTest__test3_block_impl_0((void*)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA,_weakSelf,570425344);

((void(*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char*)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char*)self +OBJC_IVAR_$_KDBlockTest$_action)));

}

block初始化的时候把 _weakSelf的地址传入,block内部对_weakSelf进行弱引用。在执行block的时候

staticvoid__KDBlockTest__test3_block_func_0(struct__KDBlockTest__test3_block_impl_0 *__cself) {__weaktypeof (self) _weakSelf = __cself->_weakSelf; // bound by copyNSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_3,((NSString *(*)(id, SEL))(void*)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),((NSString *(*)(id, SEL))(void*)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),&_weakSelf);

}

通过取得block结构体的 弱引用对象self 成员来访问相对应的方法 person2 (给对象发消息)。

__weak 成员变量上面例子,我们稍作修改:

-(void)test3

{_person2=@"person2";__weaktypeof(_person2) _weakPerson2=_person2;NSLog(@"init:%@,%p,%p",_person2,_person2,&_person2);

NSLog(@"init weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);

_action= ^(void) {//block内赋值

//_weakPerson2=@"person4";//error ,不能修改

NSLog(@"excuteing _person2:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);

};

_person2=@"person22";

NSLog(@"before:%@,%p,%p",_person2,_person2,&_person2);

NSLog(@"before weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);

_action();

NSLog(@"after:%@,%p,%p",_person2,_person2,&_person2);

}

输出日志:

2014-07-2915:29:33.472Test[2719:60b] init:person2,0x5397c,0x16566db82014-07-2915:29:33.475Test[2719:60b] init weak:person2,0x5397c,0x27db693c2014-07-2915:29:33.476Test[2719:60b] before:person22,0x539bc,0x16566db82014-07-2915:29:33.477Test[2719:60b] before weak:person2,0x5397c,0x27db693c2014-07-2915:29:33.479Test[2719:60b] excuteing _person2:person2,0x5397c,0x165b5be42014-07-2915:29:33.480Test[2719:60b] after:person22,0x539bc,0x16566db82014-07-2915:29:33.481Test[2719:60b] KDBlockTest dealloc

从日志可以看出:

直接用__weak修饰符修饰_person2变量也可以,也可以避免循环引用,但是不可以在block内部修改外部 参数的值在block外部修改变量指针指向,即把指针指向另外一块内存,block内部无法更新到。我们来看下转换后的代码:其实和不加__block的局部变量差不多,无非多了一个弱引用,不会对引用计数有影响。

struct__KDBlockTest__test3_block_impl_0 {struct__block_impl impl;struct__KDBlockTest__test3_block_desc_0*Desc;__weaktypeof (self->_person2) _weakPerson2;__KDBlockTest__test3_block_impl_0(void*fp,struct__KDBlockTest__test3_block_desc_0 *desc, __weaktypeof(self->_person2) __weakPerson2,intflags=0) : _weakPerson2(__weakPerson2) {

impl.isa= &_NSConcreteStackBlock;

impl.Flags=flags;

impl.FuncPtr=fp;

Desc=desc;

}

};

staticvoid_I_KDBlockTest_test3(KDBlockTest *self, SEL _cmd) {

(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_1;

//声明_weak 变量__attribute__((objc_gc(weak)))typeof(_person2) _weakPerson2=(*(NSString **)((char *)self +OBJC_IVAR_$_KDBlockTest$_person2));NSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_2,(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char*)self +OBJC_IVAR_$_KDBlockTest$_person2)));

NSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_3,_weakPerson2,_weakPerson2,&_weakPerson2);

//初始化block(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakPerson2, 570425344);(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_5;

NSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_6,(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char*)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char*)self +OBJC_IVAR_$_KDBlockTest$_person2)));

NSLog((NSString*)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_7,_weakPerson2,_weakPerson2,&_weakPerson2);

((void(*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char*)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char*)self +OBJC_IVAR_$_KDBlockTest$_action)));

}

在声明 _weak变量的时候,生成了一个 弱引用的指针 指向 self的person2变量。在block初始化的时候,把弱引用指针指向的内容地址 传递给了block成员

__weak typeof (self->_person2) _weakPerson2;

block结构体内部通过 成员 _weakPerson2 直接弱引用了外部变量 person2的内容地址。这时候如果把person2指针指向另外一块内存地址,那么肯定是同步不到block内部的,这个和局部变量 大同小异。

总结:

声明 __weak typeof(self) _weakSelf=self; 这样block内部 生成一个成员 ,会对self弱引用,对于值类型、引用类型都可以修改,并且修改指针指向都可以同步到任何地方。声明 __weak typeof(_person2) _weakPerson2=_person2; 针对某个具体的成员变量使用weak修饰符,这样可以避免循环引用,并且不能再block内部修改_weakPerson2。规则如下:对值类型的修改,如果block初始化后,对值类型修改,无法同步到block内部。对于引用类型的修改,如果block初始化后,修改指针指向,即指向另外一块内存,这样也是无法同步到block内部

对于引用类型的修改,如果block初始化后,对指针指向的内存进行修改,即NSMutableArray add 、remove操作,这样是可以用同步到block内部。

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航