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内部。

  • 相关阅读:
    Java 实现 蓝桥杯 生兔子问题
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 蓝桥杯 基因牛的繁殖
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    Java实现 LeetCode 33 搜索旋转排序数组
    深入探究VC —— 资源编译器rc.exe(3)
    深入探究VC —— 编译器cl.exe(2)
    深入探究VC —— 编译器cl.exe(1)
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4991630.html
Copyright © 2011-2022 走看看