ios中block的深入理解

block有三种类型:NSGlobalBlock,NSStackBlock,NSMallocBlock。

1.    NSGlobalBlock:类似函数,未引用外部变量,位于text段;

float (^ss)(float a) = ^ (float a) {

return a;

};

NSLog(@"global block %@", ss);

2.    NSStackBlock:位于栈内存,函数返回后Block将无效;

3.    NSMallocBlock:位于堆内存。


对于在堆中的block和栈中的block有的时候在使用过程中出现问题,举例分析一下:

- (void)main

int num = 888;

NSMutableArray* array =  [self getBlockArray:num];

for (int i = 0; i < array.count; i++) {

NSLog(@"aBlock %@",array[i]);

void(^bcc)() = array[i];

bcc();

}

- (NSMutableArray*) getBlockArray:(int)num

{

return [[NSMutableArray alloc] initWithObjects:

^{ NSLog(@"this is block 0:%i", num); },

^{ NSLog(@"this is block 1:%i", num); },

^{ NSLog(@"this is block 2:%i", num); },

nil];

}

      上段程序做了这几件事,得到一个含有block的数组,并进行打印。但在程序运行到 i=1 时会出现crash,原因就是EXC_BAD_ACCESS错误,出现野指针,这是为什么呢?

       block是object c 语言中的对象,只是是分配在栈上的,一般类的实例对象是分配在堆上的。就如苹果在block的文档中所说的那样:   “As an optimization, block storage starts out on the stack—just like blocks themselves do.

       但为什么数组中的第一个block可以运行通过呢?打印后你会发现,第一个block是NSMallocBlock, 是因为malloc创造的数组是在堆上的,后来的2个block还是NSStackBlock。你如果把代码改成这样,就能通过了:

- (NSMutableArray*) getBlockArray:(int)num

{

return [[NSMutableArray alloc] initWithObjects:

^{ NSLog(@"this is block 0:%i", num); },

[^{ NSLog(@"this is block 1:%i", num); } copy],

[^{ NSLog(@"this is block 2:%i", num); } copy],

nil];

}

      因为在使用copy方法后,block会被copy到堆上,你会发现,后面的两个block都变成了NSMallocBlock对象。根本原因就是,Block对象在栈上分配,block的引用指向栈帧内存,而当方法调用过后,指针指向的内存上写的是什么数据就不确定了。但是,如果你把NSMutableArray* array =  [self getBlockArray:num]; 改成

NSMutableArray* array = [[NSMutableArray alloc] initWithObjects:

^{ NSLog(@"this is block 0:%i", num); },

^{ NSLog(@"this is block 1:%i", num); },

^{ NSLog(@"this is block 2:%i", num); },

nil];

      此时,不用函数体来返回array也能通过的。因为,当一个函数调用完返回后它会释放该函数中所有的栈空间,所以函数体内的block就变成了野指针。而直接使用initWithObjects此时block还没有进行释放,换句话说,就是block还在进行引用。

      所以说,你要知道如何使用block的引用,参考以下几点:

1   Block_copy与copy等效,Block_release与release等效;

2    对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;

3    NSGlobalBlock:retain、copy、release操作都无效;

4    NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlockcopy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。

5     NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;

6     尽量不要对Block使用retain操作, 尽量使用copy。

如何理解好block的内存管理,我会在使用中慢慢理解以及消化,此次深入只是一次探讨,希望对大家有用吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 Blocks是C语言的扩充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了这...
    小人不才阅读 3,796评论 0 23
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,282评论 30 472
  • //将NSData转化为NSString NSString* str = [[NSString alloc] in...
    脱脱夫斯基阅读 1,189评论 0 52
  • 开题之前,说下MRC下的内存管理机制。引用计数式内存管理的思考方式是:1.自己生成的对象,自己持有。2.非自己生成...
    城市之光阅读 678评论 0 0
  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 950评论 0 2