zoukankan      html  css  js  c++  java
  • ios之block

    Block基本概念
    
    Block是OC中的一种数据类型
    是一个能工作的代码单元,可以在任何需要的时候被执行
    本质上是轻量级的匿名函数,可以作为其他函数的参数或者返回值
    块代码本身可能有一个参数列表,也可能有一个返回值
    可以把块代码赋给一个变量,并在需要的时候调用,就像调用一个普通函数一样
    
    块代码使用的注意事项
    默认情况下,不允许在块代码内部修改外部变量的数值
    __block
    循环引用 __weak
    
    格式说明:
    (返回类型)(^块名称)(参数类型列表) = ^(形参列表) {代码实现};
    void (^myBlock)(int,int) = ^(int x , int y) 
    {
          //代码实现        
    }
    
    //下面是Block的简单使用
    
    #import <Foundation/Foundation.h>
    
    typedef double(^MyBlock)(double, double);
    MyBlock area = ^(double x, double y) {
        return x * y;
    };
    
    int a = 0;
    static int b = 11;
    
    /** block的定义*/
    void test1()
    {
        // block 定义,是准备好一段代码片段,在需要的时候执行
        // block是C语言的格式
        // 输入:inlineblock
        // 格式:返回类型 (^blockName) (参数类型) = ^(形参列表) { 代码实现 };
        // 提示:如果没有参数,格式可以简化
        // 格式:返回类型 (^blockName) () = ^ { 代码实现 };
        void (^myBlock)() = ^{
            NSLog(@"block的最简单定义");
        };
        //调用myblock
        myBlock();
        
        //最全的定义
        int (^block) (int , int , int) = ^(int x, int y, int z)
        {
            return x + y + z;
        };
        
        int sum = 0;
        sum = block(10,19,20);
        
        NSLog(@"sum - %d",sum);
        
    }
    
    
    /** 外部访问变量,常见面试题1*/
    void test2()
    {
        int x = 10;
        __block int y = 20;
        NSLog(@"x - %d , x - add - %p",x,&x);
        
        // 在定义block时,如果block中使用到外部的“局部”变量,block会建立该变量的副本(会记录当前x的数值)
        void (^block)() = ^{
    /**********在使用局部变量时,默认不允许在块代码中直接修改局部变量的数值********/
    //        x = 20;
            
            /************全局变量可以在block中修改其值******************/
            a = 10;
            /**********如果要在block中修改外部变量的值,需要使用 __block***************/
            y = 30;
            NSLog(@"block - x = %d",x);
            NSLog(@"block - x = %p",&x); //默认存储是再栈中
        };
        block();
        NSLog(@"最后的x = %d",x);
        
    }
    
    /** 外部访问变量,常见面试题2*/
    void test3()
    {
        NSMutableString *str = [NSMutableString stringWithFormat:@"hello"];
        NSLog(@"定义之前 - %p 定义之前 - %p", &str, str);
        void (^block)() = ^{
         // 修改指针指向内存空间的内容
            [str setString:@"水水水水"];
            NSLog(@"block里面通过指针 - %p %@", &str, str);
            
        };
        
        block();
    
    }
    
    int main(int argc, const char * argv[])
    {
    
        test3();
        return 0;
    }
    

    使用注意点

    
    

    @property (nonatomic , copy) myblock testblock; //定义用copy


    1
    //水平移动(向左) 2 - (void)test6 3 { 4 CGPoint p = self.myView.center; 5 __weak typeof(self) weakSelf = self; //弱引用 6 [UIView animateWithDuration:0.9 animations:^{ 7 weakSelf.myView.center = CGPointMake(weakSelf.myView.center.x + 12, weakSelf.myView.center.y); 8 }completion:^(BOOL finished) { 9 weakSelf.testblock = ^{ 10 weakSelf.myView.center = p; 11 }; 12 }]; 13 }

    更详细的block介绍

    Objective C之Block教程

    引言

    Block是C级别的语法和运行时特性。Block比较类似C函数,但是Block比之C函数,其灵活性体现在栈内存、堆内存的引用,我们甚至可以将一个Block作为参数传给其他的函数或者Block。

    目录

    • 一、Block的基本介绍
      • 1、什么是Block
      • 2、Block和C语言函数指针和什么区别
      • 3、如何调用Block
    • 二、Block内存管理与其他特性
      • 1、Block放在哪里
      • 2、Block引用的变量在哪里
    • 三、Block揭开神秘面纱

    一、Block的基本介绍

    1、什么是Block

    Block是一个C Level的语法以及运行时的一个特性,和标准C中的函数(函数指针)类似,但是其运行需要编译器和运行时支持,从iOS4.0开始就很好的支持Block了。广泛用于两个对象之前的回调函数。

    下面我们来看一下Block的声明:

    int(^hbFunction) (BOOL a);

    其中int为block的返回类型,hbFunction为block名称,a为参数。

    2、Block和C语言函数指针和什么区别

    首先我们来看看C函数指针:

    int (* hbFunction) (int a);  // 函数声明
    int resut = hbFunction(10);  // 函数调用

    再看看Block的用法:
    int (^ hbFunction) (int a);  // 函数声明
    int resut = hbFunction(10);   // 函数调用

    C语言函数指针typedef
    typedef int (*SumFunction)(int a,int b);

    Block中的typedef
    typedef int (^SumBlock)(int a,int b); 
    3、如何调用Blocks
    主动调用一下:
    - (void)someMethod
    {
      BoolBlock ablock = ^(BOOL bValue) {
          NSLog(@"Bool block!");
      };
    
      ablock();
    }

    作为参数返回:
    typedef void (^BoolBlock)(BOOL);
    - (BoolBlock)foo()
    {
      BoolBlock ablock = ^(BOOL bValue) {
          NSLog(@"Bool block!");
      };
      return [[ablock copy] autorelease];//一定要copy,将其复制到堆上,更详细的原理,将在后续章节讲解
    }
    类的一个成员:
    @interface OBJ1 : NSObject
    @property (nonatomic, copy)BoolBlock block;//理由同上啊,同学们
    @end

    OBJ1 *obj1 = ...
    obj1.block = ^(BOOL bValue) {
              NSLog(@"Bool block!");
    };
    其他函数的参数:
    - (void)foo(BoolBlock block)
    {
      if (block) {
          block();
      }
    }
    甚至其他block的参数:
    BoolBlock bBlock = ^(BOOL bV){if(Bv){/*do some thing*/}};
    HugeBlock hBlock = ^(BoolBlock bB) {bB();};
    
    hBolck(bBlock); 

    二、Block内存管理与其他特性

    1、Block放在哪里
    1.1栈和堆

    以下情况中的block位于堆中:

    void foo()
    {
      __block int i = 1024;
      int j = 1;
      void (^blk)(void);
      void (^blkInHeap)(void);
      blk = ^{ printf("%d, %d
    ", i, j);};//blk在栈里
      blkInHeap = Block_copy(blk);//blkInHeap在堆里
    }
    
    - (void)fooBar
    {
      _oi = 1;
      OBJ1* oj = self;
      void (^oblk)(void) = ^{ printf("%d
    ", oj.oi);};
      void (^oblkInHeap)(void) = [oblk copy];//oblkInHeap在堆中
    }

    1.2全局

    以下情况中的block位于全局区
    static int(^maxIntBlock)(int, int) = ^(int a, int b){return a>b?a:b;};
    - (void)fooBar
    {
      int(^maxIntBlockCopied)(int, int) =[maxIntBlock copy];
    }
    
    void foo()
    {
      int(^maxIntBlockCopied)(int, int) = Block_copy(maxIntBlock);
    }

    需要注意的是,这里复制过后的block依旧位于全局区,实际上,复制操作是直接返回了原block对象。
    2、Block引用的变量在哪里
    1.全局区

    全局区的变量存储位置与block无关:

    static int gVar = 0;
    //__block static int gMVar = 1;
    
    void foo()
    {
      static int stackVar = 0;
    //    __block static int stackMVar = 0;
    }

    注意,static变量是不允许添加__block标记的

    2.堆栈
    void foo()
    {
      __block int i = 1024; // 此时i在栈上
      int j = 1; // 此时j在栈上
      void (^blk)(void);
      blk = ^{
          printf("%d,  %d
    ",i,j);
      };   //此时blk位于栈上,其使用的变量也在栈上
      
      blk();
      j++;
      int (^blkInHeap)(int, int) = Block_copy(blk);
      // 复制block后,block位于堆上,有__block标记的i会被复制一份至堆,而没有__block标记的j并不会动依旧位于栈上。
      
    }

    三、Block揭开神秘面纱

    1、Block到底是什么

    我们使用clang的rewrite-objc命令来获取转码后的代码。

    我们来看看最简单的一个block

    __block int i = 1024;

    int j = 1;
    void (^blk)(void);
    blk = ^{
        printf("i :%d,j:%d,&i:%p,&j:%p",i,j,&i,&j);
    };
    这个block仅仅打印栈变量i和j的值,其被clang转码为:

    truct __main_block_impl_0{
        struct __block_impl impl;
        struct __main_block_desc_0 * Desc;
        int j;
        __Block_byref_i_0 *i;
        __main_block_impl_0(void * fp, struct __main_block_desc_0 *desc,int _j,__Block_byref_i_0 *_i, int flags = 0) : j(_j),int(_i->__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_i_0 *i = __cself->i;
       int j = __cself->j;
       printf("i :%d,j:%d,&i:%p,&j:%p",i->__forwarding->i,j,&(i->__forwarding->i),&j);
    }
    
    static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){
       _Block_object_assign((void *)&dst->i, (void *)src->i,8);
    }
    
    static void __main_block_dispose_0(struct __main_block_impl_0 *src){
       _Block_object_dispose((void *)src->i,8);
    }
    
    static struct __main_block_desc_0{
       size_t reserved;
       size_t Block_size;
       void (*copy)(struct __main_block_impl_0*,struct __main_block_impl_0 *);
       void (*dispose)(struct __main_block_impl_0*);
    }__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0),__main_block_copy_0,__main_block_dispose_0};
    
    int main()
    {
       __attribute__((__blocks__(byref))) __Block_byref_i_0 = {(void *)0,(__Block_byref_i_0 *)&i,0,sizeof(__Block_byref_i_0),1024};
      int j = 1;
      void (*blk)(void);
      blk = (void (*)())& __main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA,j,(__Block_byref_i_0 *)&i,570425344);
    }


    首先是一个结构体__main_block_impl_0(从图二中的最后一行可以看到,block是一个指向__main_block_impl_0的指针,初始化后被类型强转为函数指针),其中包含的__block_impl是一个公共实现(学过c语言的同学都知道,__main_block_impl_0的这种写法表示其可以被类型强转为__block_impl类型):

    struct __block_impl {
              void *isa;
              int Flags;
              int Reserved;
              void *FuncPtr;
    };

    isa指针说明block可以成为一个objc 对象。
    __main_block_impl_0的意思是main函数中的第0个block的implementation,这就是这个block的主体了。


    这个结构体的构造函数的参数:

    • 1、block实际执行代码所在的函数的指针,当block真正被执行时,实际上是调用了这个函数,其命名也是类似的方式。

    • 2、block的描述结构体,注意这个结构体声明结束时就创建了一个唯一的desc,这个desc包含了block的大小,以及复制和析构block时需要额外调用的函数。

    • 3、接下来是block所引用到的变量们

    • 4、最后是一个标记值,内部实现需要用到的。(我用计算器看了一下,570425344这个值等于1<<29,即BLOCK_HAS_DESCRIPTOR这个枚举值)

    所以,我们可以看到:

    • 1、为什么上一篇我们说j已经不是原来的j了,因为j是作为参数传入了block的构造函数,进行了值复制。
    • 2、带有__block标记的变量会被取地址来传入构造函数,为修改其值奠定了基础

    接下来是block执行函数__main_block_func_0:

    其唯一的参数是__main_block_impl_0的指针,我们看到printf语句的数据来源都取自__cself这个指针,比较有意思的是i的取值方式(带有__block标记的变量i被转码为一个结构体),先取__forward指针,再取i,这为将i复制到堆中奠定了基础。

    再下来是预定义好的两个复制/释放辅助函数,其作用后面会讲到。

    最后是block的描述信息结构体 __main_block_desc_0,其包含block的内存占用长度,已经复制/释放辅助函数的指针,其声明结束时,就创建了一个名为__main_block_desc_0_DATA的结构体,我们看它构造时传入的值,这个DATA结构体的作用就一目了然了:

    长度用sizeof计算,辅助函数的指针分别为上面预定义的两个辅助函数。


    注意,如果这个block没有使用到需要在block复制时进行copy/retian的变量,那么desc中不会有辅助函数


    至此,一个block所有的部件我们都看齐全了,一个主体,一个真正的执行代码函数,一个描述信息(可能包含两个辅助函数)。

    2、构造一个block

    我们进入main函数:

    图一中的第三行(block的声明),在图二中,转化为一个函数指针的声明,并且都没有被赋予初始值。 而图一中的最后一行(创建一个block),在图二中,成为了对__main_block_impl_0的构造函数的调用,传入的参数的意义上面我们已经讲过了。

    所以构造一个block就是创建了__main_block_impl_0 这个c++类的实例。

    3、调用一个block

    调用一个block的写法很简单,与调用c语言函数的语法一样:

    blk();
    

     其转码后的语句:

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);


    将blk这个函数指针类型强转为block_impl类型,然后取其执行函数指针,然后将此指针类型强转为返回void*并接收一个block_impl的函数指针,最后调用这个函数,传入强转为__block_impl类型的blk,

    即调用了前述的函数__main_block_func_0

    4、objective-c类成员函数中的block

    源码如下:

    - (void)of1
    {
      OBJ1* oj = self;
      void (^oblk)(void) = ^{ printf("%d
    ", oj.oi);};
      Block_copy(oblk);
    }

    这里我故意将self赋值给oj这个变量,是为了验证前一章提出的一个结论:无法通过简单的间接引用self来防止retain循环,要避免循环,我们需要__block标记(多谢楼下网友的提醒) 转码如下:
    struct __OBJ1__of1_block_impl_0 {
              struct __block_impl impl;
              struct __OBJ1__of1_block_desc_0* Desc;
              OBJ1 *oj;
              __OBJ1__of1_block_impl_0(void *fp, struct __OBJ1__of1_block_desc_0 *desc, OBJ1 *_oj, int flags=0) : oj(_oj) {
      impl.isa = &_NSConcreteStackBlock;
      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;
              }
    };
    static void __OBJ1__of1_block_func_0(struct __OBJ1__of1_block_impl_0 *__cself) {
              OBJ1 *oj = __cself->oj; // bound by copy
          printf("%d
    ", ((int (*)(id, SEL))(void *)objc_msgSend)((id)oj,         sel_registerName("oi")));
      }

    objc方法中的block与c中的block并无太多差别,只是一些标记值可能不同,为了标记其是objc方法中的blcok。

    注意其构造函数的参数:OBJ1 *_oj

    这个oj在block复制到heap时,会被retain,而oj与self根本就是相等的,所以,最终retain的就是self,所以如果当前实例持有了这个block,retain循环就形成了。

    而一旦为其增加了__block标记:

    - (void)of1
    {
      __block OBJ1 *bSelf = self;
      ^{ printf("%d", bSelf.oi); };
    }

    其转码则变为:

    //增加了如下行

    struct __Block_byref_bSelf_0 {
              void *__isa;
      __Block_byref_bSelf_0 *__forwarding;
          int __flags;
          int __size;
          void (*__Block_byref_id_object_copy)(void*, void*);
          void (*__Block_byref_id_object_dispose)(void*);
          OBJ1 *bSelf;
    };
    
    static void __Block_byref_id_object_copy_131(void *dst, void *src) {
          _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    }
    
    static void __Block_byref_id_object_dispose_131(void *src) {
          _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    }

    //声明处变为
    __block __Block_byref_bSelf_0 bSelf = {(void*)0,(__Block_byref_bSelf_0 *)&bSelf, 33554432, sizeof(__Block_byref_bSelf_0),   __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self};
     

    clang为我们的bSelf结构体创建了自己的copy/dispose辅助函数,33554432(即1<<25 BLOCK_HAS_COPY_DISPOSE)这个值告诉系统,我们的bSelf结构体具有copy/dispose辅助函数。

    而131这个参数(二进制1000 0011,即BLOCK_FIELD_IS_OBJECT (3) |BLOCK_BYREF_CALLER(128)) 中的BLOCK_BYREF_CALLER在内部实现中告诉系统不要进行retain或者copy, 也就是说,在 __block bSelf 被复制至heap上时,系统会发现有辅助函数,而辅助函数调用后,并不retain或者copy 其结构体内的bSelf。 这样就避免了循环retain。

    2、内存管理的真面目

    objc层面如何区分不同内存区的block

    Block_private.h中有这样一组值:

    /* the raw data space for runtime classes for blocks */
    /* class+meta used for stack, malloc, and collectable based blocks */
    BLOCK_EXPORT void * _NSConcreteStackBlock[32];
    BLOCK_EXPORT void * _NSConcreteMallocBlock[32];
    BLOCK_EXPORT void * _NSConcreteAutoBlock[32];
    BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];
    BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
    BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32];

    其用于对block的isa指针赋值 1.栈

    truct __OBJ1__of2_block_impl_0 {
              struct __block_impl impl;
              struct __OBJ1__of2_block_desc_0* Desc;
              OBJ1 *self;
              __OBJ1__of2_block_impl_0(void *fp, struct __OBJ1__of2_block_desc_0 *desc, OBJ1 *_self, int flags=0) : self(_self) {
          impl.isa = &_NSConcreteStackBlock;
          impl.Flags = flags;
          impl.FuncPtr = fp;
          Desc = desc;
              }
    };

    在栈上创建的block,其isa指针是_NSConcreteStackBlock。

    2.全局区

    在全局区创建的block,其比较类似,其构造函数会将isa指针赋值为_NSConcreteGlobalBlock。

    3.堆

    我们无法直接创建堆上的block,堆上的block需要从stack block拷贝得来,在runtime.c中的_Block_copy_internal函数中,有这样几行:

    // Its a stack block.  Make a copy.
    if (!isGC) {
        struct Block_layout *result = malloc(aBlock->descriptor->size);
        ...
        result->isa = _NSConcreteMallocBlock;
        ...
        return result;
    }

    可以看到,栈block复制得来的新block,其isa指针会被赋值为_NSConcreteMallocBlock

    4.其余的isa类型
    BLOCK_EXPORT void * _NSConcreteAutoBlock[32];
    BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32];
    BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32];

    其他三种类型是用于gc和arc,我们暂不讨论

    复制block

    对block调用Block_copy方法,或者向其发送objc copy消息,最终都会调用runtime.c中的_Block_copy_internal函数,其内部实现会检查block的flag,从而进行不同的操作:

    static void *_Block_copy_internal(const void *arg, const int flags) {
      ...
      aBlock = (struct Block_layout *)arg;
      ...
    }

    1.栈block的复制

     // reset refcount
     result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
     result->flags |= BLOCK_NEEDS_FREE | 1;
     result->isa = _NSConcreteMallocBlock;
     if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
         //printf("calling block copy helper %p(%p, %p)...
    ", aBlock->descriptor->copy, result, aBlock);
         (*aBlock->descriptor->copy)(result, aBlock); // do fixup
     }

    除了修改isa指针的值之外,拷贝过程中,还会将BLOCK_NEEDS_FREE置入,大家记住这个值,后面会用到。 最后,如果block有辅助copy/dispose函数,那么辅助的copy函数会被调用。

    2.全局block的复制

    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }

    全局block进行copy是直接返回了原block,没有任何的其他操作。

    3.堆block的复制

    f (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }

    栈block复制时,置入的BLOCK_NEEDS_FREE标记此时起作用,_Block_copy_internal函数识别当前block是一个堆block,则仅仅增加引用计数,然后返回原block。

    辅助copy/dispose函数

    1.普通变量的复制 辅助copy函数用于拷贝block所引用的可修改变量,我们这里以 __block int i = 1024为例: 先看看Block_private.h中的定义:

    struct Block_byref {
      void *isa;
      struct Block_byref *forwarding;
      int flags; /* refcount; */
      int size;
      void (*byref_keep)(struct Block_byref *dst, struct Block_byref *src);
      void (*byref_destroy)(struct Block_byref *);
      /* long shared[0]; */
    };

    而我们的__block int i = 1024的转码:

    struct __Block_byref_i_0 { void *__isa; __Block_byref_i_0 *__forwarding; int __flags; int __size; int i; };//所以我们知道,当此结构体被类型强转为Block_byref时,前四个成员是一致的,访问flags就相当于访问__flags,而内部实现就是这样使用的 ... __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0, (__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 1024};//i初始化时__flags为0 static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->i, (void*)src->i, 8/*BLOCK_FIELD_IS_BYREF*/);}

    此时,复制时调用的辅助函数:

    void _Block_object_assign(void *destAddr, const void *object, const int flags) {//此处flags为8,即BLOCK_FIELD_IS_BYREF
    ...
      if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
          // copying a __block reference from the stack Block to the heap
          // flags will indicate if it holds a __weak reference and needs a special isa
              _Block_byref_assign_copy(destAddr, object, flags);
      }
    ...
    }
    
    static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {//此处flags为8,即BLOCK_FIELD_IS_BYREF
      struct Block_byref **destp = (struct Block_byref **)dest;
      struct Block_byref *src = (struct Block_byref *)arg;
      ...
      else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {//当初次拷贝i时,flags为0,进入此分支会进行复制操作并改变flags值,置入BLOCK_NEEDS_FREE和初始的引用计数
       ...
      }
      // already copied to heap
      else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {//当再次拷贝i时,则仅仅增加其引用计数
          latching_incr_int(&src->forwarding->flags);
      }
      // assign byref data block pointer into new Block
      _Block_assign(src->forwarding, (void **)destp);//这句仅仅是直接赋值,其函数实现只有一行赋值语句,查阅runtime.c可知
    }

    所以,我们知道,当我们多次copy一个block时,其引用的__block变量只会被拷贝一次。

    2.objc变量的复制

    当objc变量没有__block修饰时:

    static void __OBJ1__of2_block_copy_0(struct __OBJ1__of2_block_impl_0*dst, struct __OBJ1__of2_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}
    
    void _Block_object_assign(void *destAddr, const void *object, const int flags) {
      ...
      else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
          //printf("retaining object at %p
    ", object);
          _Block_retain_object(object);//当我们没有开启arc时,这个函数会retian此object
          //printf("done retaining object at %p
    ", object);
          _Block_assign((void *)object, destAddr);
      }
      ....
    }

    当objc变量有__block修饰时:
    struct __Block_byref_bSelf_0 { void *__isa; __Block_byref_bSelf_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); OBJ1 *bSelf; }; static void __Block_byref_id_object_copy_131(void *dst, void *src) { _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);//131即为BLOCK_FIELD_IS_OBJECT|BLOCK_BYREF_CALLER } static void __Block_byref_id_object_dispose_131(void *src) { _Block_object_dispose(*(void * *) ((char*)src + 40), 131); } ... //33554432即为BLOCK_HAS_COPY_DISPOSE __block __Block_byref_bSelf_0 bSelf = {(void*)0,(__Block_byref_bSelf_0 *)&bSelf, 33554432, sizeof(__Block_byref_bSelf_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, self};
     
     
     转自于http://www.beyondabel.com/blog/2014/02/15/ios-block/

      

     
  • 相关阅读:
    洛谷 P1508 Likecloud-吃、吃、吃
    Codevs 1158 尼克的任务
    2017.10.6 国庆清北 D6T2 同余方程组
    2017.10.6 国庆清北 D6T1 排序
    2017.10.3 国庆清北 D3T3 解迷游戏
    2017.10.3 国庆清北 D3T2 公交车
    2017.10.3 国庆清北 D3T1 括号序列
    2017.10.4 国庆清北 D4T1 财富
    2017.10.7 国庆清北 D7T2 第k大区间
    2017.10.7 国庆清北 D7T1 计数
  • 原文地址:https://www.cnblogs.com/ndyBlog/p/3958956.html
Copyright © 2011-2022 走看看