zoukankan      html  css  js  c++  java
  • Block深入浅出

      • 研究工具
        • clang 为了研究编译器的实现原理,我们需要使用 clang 命令。clang 命令可以将 Objetive-C 的源码改写成 C / C++ 语言的,借此可以研究 block 中各个特性的源码实现方式。
        • clang -rewrite-objc main.m
        • main.m中不能包含UIKit框架,命令行中解析无法识别。包含#import <Foundation/Foundation.h>是可以支持的
      • C语言中变量有哪几种
        • 自动变量
        • 函数参数 
        • 静态变量
        • 静态全局变量
        • 全局变量
      • 每种变量类型在Block中的特性及原理
        • 自动变量
          • 不可以修改,携带__block修饰可以被修改
          • 会被Block持有(retainCount+1)
          • 不带__block修饰的会被copy进Block
        • 函数参数 
          • 可以直接修改
          • 不会被Block持有(retainCount不会增加)
        • 静态变量
          • 可以被修改 - 由于传递给Block是内存地址值,查看Block的具体实现(查看clang后的main.cpp文件)
        • 静态全局变量和全局变量
          • 可以直接被访问和修改 - 由于存储区域在区全局区,由于作用区域的原因
          • 不会被Block持有(retainCount不会增加)
      • Block中改变变量值的方式
        • 传递内存地址到Block
          • 指针所指向的内存不可修改,但是内存中存放的数据可以修改
          • NSMutableString 变量可以直接在Block体中被appendString,但是不可以被=
        • 使用__block修饰
          • Block会将此标识符修饰的变量转化成一个结构体,Block体中传递并且使用的是这个结构体
          • __block int i 会被转换成
          • struct __Block_byref_i_0 {
              void *__isa; //指向自己
            __Block_byref_i_0 *__forwarding; //指向自己,当被copy到堆(heap)上时,原Block此字段指向堆上的Block地址,对上的此字段仍然指向自己。这样不管__block怎么复制到堆上,还是在栈上,都可以通过(i->__forwarding->i)来访问到变量值。
             int __flags;
             int __size;
             int i;
            };
             
        • Block捕获外部变量仅仅只捕获Block闭包里面会用到的值,其他用不到的值,它并不会去捕获。而且Block能捕获的变量只有自动变量和静态变量了。
      • Block的种类
        • _NSConcreteStackBlock
          • 只用到外部局部变量、成员属性变量,且没有强指针引用的block都是StackBlock
          • StackBlock的生命周期由系统控制的,一旦返回之后,就被系统销毁了
          • 不持有对象
          • 对Block的retain,release造作无效,copy造作会变成_NSConcreteMallocBlock类型
        • _NSConcreteMallocBlock
          • 有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock
          • 没有强指针引用即销毁,生命周期由程序员控制
          • 持有对象
          • retain,release,copy操作生效,内存管理器中的计数会增加。(但retainCount始终为1)
        • _NSConcreteGlobalBlock
          • 没有用到外界变量或只用到全局变量、静态变量的block为_NSConcreteGlobalBlock
          • 生命周期从创建到应用程序结束
          • 不持有对象
          • retain,release,copy操作为空操作
        • ARC下,系统会根据下面的规则决定是否将Block复制到heap上
      • 系统调用copy对Block复制的情况
        • 手动调用copy(当Block为函数参数的时候,就需要我们手动的copy一份到堆上了。这里除去系统的API我们不需要管,比如GCD等方法中本身带usingBlock的方法)
        • Block是函数的返回值
        • Block被强引用(Block被赋值给__strong或者id类型)
        • 调用系统API入参中含有usingBlcok的方法
      • __block堆栈拷贝
        • MRC 只有发生了copy,__block修饰的对象才会被copy到堆上
        • ARC 发生了copy或者=(block 类型通过=进行传递时,会导致调用objc_retainBlock->_Block_copy->_Block_copy_internal方法链),__block修饰的对象才会被copy到堆上
        • __block修饰的对象才会被copy到堆上 : __NSStackBlock__ 类型的 block 转换为 __NSMallocBlock__ 类型
      • clang代码转换
        • main.m 文件30行,大小831字节。转换后main.cpp 文件104810行,大小3.1MB。
      • Block 循环引用
        • 引起循环引用的条件其实很苛刻:
          • Block需要被相关类(当前类或者嵌套引用的某各类)retain或copy等类似操作
          • Block体中使用self(包括成员变量,成员属性等)
        • 发生循环引用的拆解方式:
          • 使用__weak对self进行弱引用,其实是通过弱引用的方式将闭环解开
            • __weak __typeof(self) wself = self;
              self.myBlock = ^{
                  __strong __typeof(wself) self = wself;
                  // 使用self进行相关操作即可
              };
          • 使用形参的方式,将self作为参数传递给Block
        • 常见易混淆的场景(前提:Block没有被retain或copy的情况下,即苛刻条件中的第一条)
          • GCD,系统动画等系统Block API,Block体中直接使用self不会有问题
          • Block体中使用了成员属性或者成员变量,不会有问题 (参考Block种类)
          • 访问了静态变量,全局变量,全局静态变量,不会引起问题
  • 相关阅读:
    敏捷开发模式下的质量管理
    杨学明老师软件测试管理公开课将于2012年11月16日~17日在北京举办!
    杨学明老师为南京某机电企业成功举办两天IPD DRY RUN !
    软件测试管理公开课在北京成功举办!
    2012年12月4至6日,杨学明老师为中国科学院某研究所举办两天的软件项目管理和测试管理培训!
    杨老师“软件测试管理”公开课在北京成功举办!
    软件测试为什么失败?
    如何实现高效的产品测试管理杨学明
    杨学明老师软件测试管理公开课将于2012年9月21~22日在深圳举办!
    2011年9月23《软件测试管理》公开课,接受报名!
  • 原文地址:https://www.cnblogs.com/jinfengboy/p/5844646.html
Copyright © 2011-2022 走看看