zoukankan      html  css  js  c++  java
  • block 解析

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

    我们了解到了用__block修饰的变量,可以在block内部修改,__block变量其实对应一个结构体

    1. struct __Block_byref__para1_0 { 
    2.   void *__isa; 
    3. __Block_byref__para1_0 *__forwarding; 
    4.  int __flags; 
    5.  int __size; 
    6.  char *_para1; 
    7. }; 

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

    1. struct __main1_block_impl_0 { 
    2.   struct __block_impl impl; 
    3.   struct __main1_block_desc_0* Desc; 
    4.   __Block_byref__para1_0 *_para1; // by ref 

    block内部 成员变量 使用,可以在block内部修改,对应一个、多个成员变量参数,block结构体内部有一个成员引用

    1. struct __KDBlockTest__test3_block_impl_0 { 
    2.   struct __block_impl impl; 
    3.   struct __KDBlockTest__test3_block_desc_0* Desc; 
    4.   KDBlockTest *self; 

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

    循环引用

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

    我们看以下代码(ARC):

    1. typedef void(^ActionTest)(void); 
    2. @interface KDBlockTest() 
    3.     __block NSString *_person2; 
    4.     ActionTest _action; 
    5. }
    1. @implementation KDBlockTest 
    2. #pragma mark - system 
    3. -(instancetype)init 
    4.     self=[super init]; 
    5.     if(self) 
    6.     { 
    7.         [self test3]; 
    8.     } 
    9.     return self; 
    10. -(void)dealloc 
    11.     NSLog(@"KDBlockTest dealloc"); 
    12.  
    13. #pragma mark - private 
    14. ////循环引用 
    15. -(void )test3 
    16.     _person2=@"person2"; 
    17.     _action= ^(void) { 
    18.         //block内赋值 
    19.         NSLog(@"excuteing _person2:%@,%p",_person2,_person2); 
    20.     }; 
    21.     _action(); 

    这样我们执行以下代码:

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

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

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

    ——weak

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

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

    1. -(void )test3 
    2.     __weak typeof(self) _weakSelf=self; 
    3.     _person2=@"person2"; 
    4.     NSLog(@"init:%@,%p,%p",_person2,_person2,&self); 
    5.     _action= ^(void) { 
    6.         //block内赋值 
    7.         NSLog(@"excuteing _person2:%@,%p,%p",_weakSelf.person2,_weakSelf.person2,&_weakSelf); 
    8.     }; 
    9.     _action(); 

    输出日志:

    2014-07-29 13:38:30.872 Test[2642:60b] init:person2,0x5b980,0x27dae944
    2014-07-29 13:38:30.875 Test[2642:60b] excuteing _person2:person2,0x5b980,0x1562ed44
    2014-07-29 13:38:30.876 Test[2642:60b] KDBlockTest dealloc

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

    block结构体的定义:

    1. struct __KDBlockTest__test3_block_impl_0 { 
    2.   struct __block_impl impl; 
    3.   struct __KDBlockTest__test3_block_desc_0* Desc; 
    4.   __weak typeof (self) _weakSelf; 
    5.   __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self) __weakSelf, int flags=0) : _weakSelf(__weakSelf) { 
    6.     impl.isa = &_NSConcreteStackBlock; 
    7.     impl.Flags = flags; 
    8.     impl.FuncPtr = fp; 
    9.     Desc = desc; 
    10.   } 
    11. }; 

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

    1. static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) { 
    2.     __attribute__((objc_gc(weak))) typeof(self) _weakSelf=self; 
    3.     (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_1; 
    4.     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); 
    5.     (*(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); 
    6.     ((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的时候

    1. static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself) { 
    2.   __weak typeof (self) _weakSelf = __cself->_weakSelf; // bound by copy 
    3.  
    4.  
    5.         NSLog((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); 
    6.     } 

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

    —weak变量

    上面例子,我们稍作修改:

    1. (void )test3 
    2.     _person2=@"person2"; 
    3.     __weak typeof(_person2) _weakPerson2=_person2; 
    4.     NSLog(@"init:%@,%p,%p",_person2,_person2,&_person2); 
    5.     NSLog(@"init weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); 
    6.     _action= ^(void) { 
    7.         //block内赋值 
    8. //_weakPerson2=@"person4";//error ,不能修改 
    9.  
    10.         NSLog(@"excuteing _person2:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); 
    11.     }; 
    12.     _person2=@"person22"; 
    13.     NSLog(@"before:%@,%p,%p",_person2,_person2,&_person2); 
    14.     NSLog(@"before weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2); 
    15.     _action(); 
    16.     NSLog(@"after:%@,%p,%p",_person2,_person2,&_person2); 

    输出日志:

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

    从日志可以看出:

    1. 直接用__weak修饰符修饰_person2变量也可以,也可以避免循环引用,但是不可以在block内部修改外部 参数的值
    2. 在block外部修改变量指针指向,即把指针指向另外一块内存,block内部无法更新到。

    我们来看下转换后的代码:其实和不加__block的局部变量差不多,无非多了一个弱引用,不会对引用计数有影响。

    1. struct __KDBlockTest__test3_block_impl_0 { 
    2.   struct __block_impl impl; 
    3.   struct __KDBlockTest__test3_block_desc_0* Desc; 
    4.   __weak typeof (self->_person2) _weakPerson2; 
    5.   __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self->_person2) __weakPerson2, int flags=0) : _weakPerson2(__weakPerson2) { 
    6.     impl.isa = &_NSConcreteStackBlock; 
    7.     impl.Flags = flags; 
    8.     impl.FuncPtr = fp; 
    9.     Desc = desc; 
    10.   } 
    11. }; 
    1. static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) { 
    2.  
    3.     (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_1; 
    4. //声明_weak 变量 
    5.     __attribute__((objc_gc(weak))) typeof(_person2) _weakPerson2=(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)); 
    6.     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))); 
    7.     NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_3,_weakPerson2,_weakPerson2,&_weakPerson2); 
    8. //初始化block 
    9.     (*(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); 
    10.     (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_5; 
    11.     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))); 
    12.     NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_7,_weakPerson2,_weakPerson2,&_weakPerson2); 
    13.     ((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内部的,这个和 局部变量  大同小异。

    总结:

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

         

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

  • 相关阅读:
    centos7系统修改内核
    使用yum update更新文件系统时不更新内核的方法
    实现让用户不断的猜年龄,但只给最多3次机会,超过次数猜不对就退出程序。
    yum安装软件中的y/d/N
    MySQL5.7.15数据库配置主从服务器实现双机热备实例教程
    CentOS7.2 多个python版本共存
    CentOS 7.2 安装Python3.5.2
    R语言与概率统计(五) 聚类分析
    R语言与概率统计(四) 判别分析(分类)
    R语言与概率统计(三) 多元统计分析(下)广义线性回归
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4991630.html
Copyright © 2011-2022 走看看