Block小结

一年又见底了,一年收集的资料文档,趁现在的空闲会陆续的整理出来。。。

<二、Block小结>
在类中声明一个block为什么要用copy修饰的话,那就要先说block的三种类型。

1._NSConcreteGlobalBlock,全局的静态block,不会访问外部的变量。就是说如果你的block没有调用其他
的外部变量,那你的block类型就是这种。

2._NSConcreteStackBlock
保存在栈中的 block,当函数返回时会被销毁。这个block就是你声明的时候不用c
opy修饰,并且你的block访问了外部变量。

3._NSConcreteMallocBlock
保存在堆中的 block,当引用计数为 0 时会被销毁。好了,这个就是今天的主角 ,用copy修饰的block。

下面是有参数与无参数的Block调用
前提条件:需要将测试的VC改成MRC模式,需要点击项目的TARGETS->BuIld Phases下面测试的VC后面添加-fno-objc-arc即可

  //__NSGlobalBlock__ 全局区的 (没有引用外部变量)
  void (^BlockTest1)() = ^{
  };
  NSLog(@"%@",BlockTest1);


  int i = 6;
  //__NSStackBlock__ 栈区 (内部使用了外部变量)
  void (^BlockTest2)() = ^{
    NSLog(@"使用的外部变量为 %d",i);
  };
  NSLog(@"BlockTest2 %@",BlockTest2);


  //__NSMallocBlock__ 堆区 ([DemoBlock2 copy]后Block存放在堆区)
  NSLog(@"BlockTest2.Copy %@",[BlockTest2 copy]);
  void (^BlockTest3)() = [BlockTest2 copy];
  NSLog(@"BlockTest3 %@",BlockTest3);

block本身是像对象一样可以retain,和release。但是,block在创建的时候,它的内存是分配在栈(stack)上,而不是在堆(heap)上。他本身的作于域是属于创建时候的作用域,一旦在创建的时候作用域外面调用block将导致程序崩溃。
使用retain也可以,但是block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。

下面可以看下Block的具体实现,其实就是一个.cpp(c++)的文件,首先用终端cd 获取到你将要转译文件的的当前文件夹,然后在终端输入clang -rewrite-objc 要编译的文件名.m,成功后你会在对应的文件夹中看到一个名称相同的.cpp文件
源文件:无参数调用

int main(int argc, char * argv[]) {
  @autoreleasepool {
    //__NSGlobalBlock__ 全局区的 (没有引用外部变量)
    void (^BlockTest1)() = ^{
    NSLog(@"全局区的Block");
  };
  BlockTest1();
  }
  return 0;
}
转译后的.cpp文件为

// block的实现,也是入口

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

 

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;

  // Block的结构体
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock; // isa指针,指向Block自己的指针
    impl.Flags = flags; // 标记
    impl.FuncPtr = fp; // 
    Desc = desc; // 
  }
};

 

// Block函数回调执行的地方
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_bb_s0jryjyj11l3b3gktdj2sylc0000gn_T_main_7f3244_mi_0);
}

 

// block的描述信息的结构体

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

 

// mian函数入口
int main(int argc, char * argv[]) {
  /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

  // Block函数的定义

  void (*BlockTest1)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

  // Block函数的调用
  ((void (*)(__block_impl *))((__block_impl *)BlockTest1)->FuncPtr)((__block_impl *)BlockTest1);
}
return 0;
}

 

源文件:有参数调用

int main(int argc, char * argv[]) {
  @autoreleasepool {
    //__NSStackBlock__ 栈区 (内部使用了外部变量)
    __block int i = 6;
    void (^BlockTest2)() = ^{
      NSLog(@"使用的外部变量为 %d",i);
    };
  i = 9;
  BlockTest2();
  }
  return 0;
}

转译后的.cpp文件为:

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

 

// 这个便是__block i = 6,在c++中的表现形式,其实就是一个结构体

struct __Block_byref_i_0 {
  void *__isa;// isa 类型指针 指向自己
  __Block_byref_i_0 *__forwarding;// 存放的是自己的地址
  int __flags;// 标记
  int __size;// 类型大小
  int i;// 属性变量
};

 

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_i_0 *i; // by ref 参数值得传递
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

 

// block函数的回调方法
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

  // 这里访问的内存的值为9而不是6,它在main()函数里面已做了改变
  __Block_byref_i_0 *i = __cself->i; // bound by ref

  NSLog((NSString *)&__NSConstantStringImpl__var_folders_bb_s0jryjyj11l3b3gktdj2sylc0000gn_T_main_68164f_mi_0,(i->__forwarding->i));
}


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*/);

}

 

static void __main_block_dispose_0(struct __main_block_impl_0*src) {

  _Block_object_dispose((void*)src->i,

   8/*BLOCK_FIELD_IS_BYREF*/);

}

 

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(int argc, char * argv[]) {
  /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;

     // (__Block_byref_i_0 *)&i 传递了参数i的内存地址

    __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 6};
    void (*BlockTest2)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

    // 内存地址值的得改变
    (i.__forwarding->i) = 9;
    ((void (*)(__block_impl *))((__block_impl *)BlockTest2)->FuncPtr)((__block_impl *)BlockTest2);
  }
  return 0;
}

posted @ 2018-01-22 17:45  雪飞雨落  阅读(101)  评论(0编辑  收藏  举报