iOS block的深度探究

tags: block

分两部分内容来剖析block:

  1. 怎么用
  2. 为什么这么用

block的堆栈

分类:

根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。

NSGlobalBlock:类似函数,位于text段; NSStackBlock:位于栈内存,函数返回后Block将无效; NSMallocBlock:位于堆内存。需要开发者进行释放。
区分:非ARC下,无引用外部变量的即为NSGlobalBlock,引用外部变量的为NSStackBlock,NSStakBlock做copy即为NSMallocBlock。
内存管理:

block是编译时生成的,而不是运行时生成的。所以在编译时如果用到外部变量就会把变量建一份快照放到栈上。

  • NSGlobalBlock:生命周期从应用程序开始到程序终止。对其做retain/release/copy都没有任何意义,还是返回本身。
  • NSStackBlock:函数返回后即消失。对其做retain/release都没有任何意义,还是返回本身。对其做copy则会把内容复制到堆内存(NSMallocBlock),生成新的内存块。
  • NSMallocBlock:需要程序员自己释放,对其做retain和copy一样,只是所指对象计数器增加1,打印时一直会显示1,但实际计数器已经增加了。对其做release计数器减一,不过打印计数器时,一直还是显示1。

 

typedef int (^square)(int);

(void) viewDidLoad
{
[super viewDidLoad];

square tempBlock1 = ^(int a){ return a * a; };

NSLog(@"tempBlock1:%@, return1:%d", tempBlock1, tempBlock1(5));
// log: tempBlock1:<__NSGlobalBlock__: 0x10e6d9240>, return1:25

int i = 2;
square temptBlock2 = ^(int a){ int itRet = i * a; return itRet; };
NSLog(@"temptBlock2:%@, return2:%d", temptBlock2, temptBlock2(5));
// log: temptBlock2:<__NSStackBlock__: 0x7fff51528a50>, return2:10

square temptBlock3 = [temptBlock2 copy];
NSLog(@"temptBlock3:%@, return3:%d", temptBlock3, temptBlock3(5));
// log: temptBlock3:<__NSMallocBlock__: 0x7fbdc0608180>, return3:10

temptBlock = [temptBlock3 copy];
NSLog(@"temptBlock:%@, return:%d", temptBlock, temptBlock(5));
// log: temptBlock:<__NSMallocBlock__: 0x7fbdc0608180>, return3:10 此时temptBlock的计数器实际上是2

[temptBlock release];
}

(IBAction)Test:(id)sender {
NSLog(@"=====temptBlock:%@, return:%d, blockRec:%ld", temptBlock, temptBlock(5), [temptBlock retainCount]);

// 如果viewDidLoad中temptBlock再做次release就crash了 这里
}

 

修改变量值原理

 

typedef int (^square)(int);

int i = 6;
square tempBlock1 = ^(int a){return a * i; };

printf("=====%d", tempBlock1(5));

如果想在tempBlock1中更改i值是不被允许的,为什么?查看代码的底层实现:clang -rewrite-objc block2.c,去掉不必要的代码,保留主要代码:

 

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;
int i;
__main_block_impl_0(void fp, struct __main_block_desc_0 desc, int _i, int flags=0) : i(_i) {

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. Desc = desc;

}
};

static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
int i = __cself->i; // bound by copy
return a * i; }

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)};

int main()
{

 
  1. int i = 6;

  2. square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, i));

  3.  
  4. printf("=====%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5));

  5.  
  6. return 0;

}

代码看上去有些多,看主要信息:

  1. __block_impl:isa指名是一个对象,指向所属类的指针也就是保存了block的类型(这里为栈对象),flags记录block的标识,reserved:保留字段为了扩展,funcptr:block执行的函数体,也就是block的实现。__main_block_impl_0:包含__block_impl对象和__main_block_desc_0(就是记录__main_block_impl_0的大小),以及外部变量的映像。个人理解其实__block_impl就是__main_block_impl_0的基类。
  2. 顺序:创建tempBlock1对象,执行block的函数体。
  3. 为什么不能在block中直接更改变量值,因为传的只是形参,如果改变的话只能操作tempBlock1中的i的改变,但是外部的变量i是不会被改变的,所以干脆禁止这么操作。那么怎么改才能和外部的联动?

 

{
  
 
  1. __block int i = 6;

  2. square tempBlock1 = ^(int a){ i = i + 1; return a * i; };

  3.  
  4. i = 7;

  5.  
  6. printf("=====%d,i=%d", tempBlock1(5), i);

  7.  
  8. return 0;

}

同样查看源码,去掉不必要的代码:

 

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

struct __Block_byref_i_0 {
void *__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) {

 
  1. impl.isa = &_NSConcreteStackBlock;

  2. impl.Flags = flags;

  3. impl.FuncPtr = fp;

  4. Desc = desc;

}
};

static int __main_block_func_0(struct __main_block_impl_0 *__cself, int a) {
__Block_byref_i_0 *i = __cself->i; // bound by ref
(i->__forwarding->i) = (i->__forwarding->i) + 1; return a * (i->__forwarding->i); }

static void __main_block_copy_0(struct __main_block_impl_0dst, struct __main_block_impl_0src) {_Block_object_assign((void)&dst->i, (void)src->i, 8/BLOCK_FIELD_IS_BYREF/);}

static void __main_block_dispose_0(struct __main_block_impl_0src) {_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()
{

 
  1. __attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0 *)&i, 0, sizeof(__Block_byref_i_0), 6};

  2. square tempBlock1 = ((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_i_0 *)&i, 570425344));

  3.  
  4. (i.__forwarding->i) = 7;

  5.  
  6. printf("=====%d,i=%d", ((int (*)(__block_impl *, int))((__block_impl *)tempBlock1)->FuncPtr)((__block_impl *)tempBlock1, 5), (i.__forwarding->i));

  7.  
  8. return 0;

}

只看不同的代码:

  1. 多了个__Block_byref_i_0:生成__B
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值