如何在iosios block中使用selfblock

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
标签:至少1个,最多5个
什么是 block
Blocks 是 iOS 4.0 之后有的新功能。Block 能够让我们的代码变得更简单,能够减少代码量,降低对于 delegate 的依赖,还能够提高代码的可读性。
本质上来说,一个 Block 就是一段能够在将来被执行的代码。本身 Block 就是一个普通的 Objective-C 对象。正因为它是对象,Block 可以被作为参数传递,可以作为返回值从一个方法返回,可以用来给变量赋值。在其他语言(Python, Ruby, Lisp etc.)中,Block 被叫做闭包——因为他们在被声明的时候的封装状态。Block 为指向它内部的局部变量创造了一个常量 copy。
在 Block 之前,如果我们想要调用一段代码,然后之后一段时间,让它给我们返回,我们一般会使用 delegate 或者 NSNotification。这样的写法没什么问题,但是使用过 delegate 和 NSNotification 大家就应该会感觉到——我们会不可避免的将代码写的到处都是,我们需要在某处开始一个任务,在另外一个地方来处理这个返回结果。使用 Block 就可以在一定程度上避免这个问题。
如何使用 Block
下面这张图片来自苹果官方文档:
Block 的声明格式如下:
return_type (^block_name)(param_type, param_type, ...
^ 符号其实就是专门用来表示:我们在声明一个 Block。声明举例:
int (^add)(int,int)
block 的定义格式如下:
^return_type(param_type param_name, param_type param_name, ...) { ... return return_ }
要注意,定义和声明的格式有一些不同。定义以 ^ 开始,后面跟着参数(参数在这里一定要命名),顺序和类型一定要和声明中的顺序一样。定义时,返回值类型是 optional 的,我们可以在后面的代码中确定返回值类型。如果有多个返回 statement,他们也只能有一个返回值类型,或者把他们转成同一个类型。block 的定义举例:
^(int number1, int number2){ return number1+number2 }
我们把声明和定义放在一起:
int (^add)(int,int) = ^(int number1, int number2){
return number1+number2;
调用的时候:
int resultFromBlock = add(2,2);
我们将使用 block 与不使用 block 做一些对比
举例 :NSArray普通 for 循环:
for (int i = 0 ; i & [theArray count] ; i++) {
NSLog(@"The object at index %d is %@",i,[theArray objectAtIndex:i]);
这个 BOOL stop 现在看上去有点奇怪,但看到后面 block 实现就能理解了
快速迭代:
int idx = 0;
for (id obj in theArray) {
NSLog(@"The object at index %d is %@",idx,obj);
使用 block :
[theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(@"The object at index %d is %@",idx,obj);
在上面的代码中, BOOL stop 设置为 YES 的时候,可以从block 内部停止下一步运行。从上面三段代码的对比中,我们可以至少可以看出 block 两方面的优势:
简化了代码
提高了速度
举例:UIview Animation
非 Block 实现
-(void)removeAnimationView:(id)sender {
[animatingView removeFromSuperview];
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[UIView beginAnimations:@"Example" context:nil];
[UIView setAnimationDuration:5.0];
[UIView setAnimationDidStopSelector:@selector(removeAnimationView)];
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0,
animatingView.center.y+50.0)];
[UIView commitAnimations];
block 实现
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[UIView animateWithDuration:5.0
animations:^{
[animatingView setAlpha:0];
[animatingView setCenter:CGPointMake(animatingView.center.x+50.0,
animatingView.center.y+50.0)];
completion:^(BOOL finished) {
[animatingView removeFromSuperview];
同样我们可以看出 block 的优势:简化了代码
让代码保持在一起,不需要在一个地方开始动画,在另一个地方回调。读写起来都比较方便。苹果也建议这么做,不然苹果用 block 重写以前的代码干嘛呢~
block 的应用
1. enumerateObjectsUsingBlock
之前的代码实例中已经提到过,用来迭代数组十分方便,具体看下面的代码实例:
-(NSArray*)retrieveInventoryItems {
// 1 - 声明
NSMutableArray* inventory = [NSMutableArray new];
NSError* err =
// 2 - 得到 inventory 数据
NSArray* jsonInventory = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:kInventoryAddress]]
options:kNilOptions
error:&err];
// 3 - 使用 block 遍历
[jsonInventory enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSDictionary* item =
[inventory addObject:[[IODItem alloc] initWithName:[item objectForKey:@"Name"]
andPrice:[[item objectForKey:@"Price"] floatValue]
andPictureFile:[item objectForKey:@"Image"]]];
// 4 - 返回一个 inventory 的 copy
return [inventory copy];
我们在上面的代码中 3 处使用了 block:
使用了 enumerateObjectsUsingBlock 方法,把一个普通的 NSDictionary 转化成一个 IODItem 类的对象。我们对一个JSON Array 对象发送 enumerateObjectsUsingBlock 消息,迭代这个 array,得到 item 字典,然后用这个字典得到 IODItem,最后把这些对象添加到 inventory 数组然后返回。
2.sortedArrayUsingComparator
enumerateObjectsUsingBlock我们上面已经用过,主要来看 sortedArrayUsingComparator ,这个 block 以一个升序返回一个 array,这个升序由一个 NSComparator block 决定
注意:compare 方法的使用有点没太明白,但是根据 sortedArrayUsingComparator 是返回一个升序数组,所以compare 方法应该是返回两者之间更大的??
-(NSString*)orderDescription {
// 1 - 声明
NSMutableString* orderDescription = [NSMutableString new];
// 2 - 使用 block 进行排序
NSArray* keys = [[self.orderItems allKeys] sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
IODItem* item1 = (IODItem*)obj1;
IODItem* item2 = (IODItem*)obj2;
return [item1.name compare:item2.name];
// 3 - 使用 block 遍历
[keys enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
IODItem* item = (IODItem*)
NSNumber* quantity = (NSNumber*)[self.orderItems objectForKey:item];
[orderDescription appendFormat:@"%@ x%@\n", item.name, quantity];
// 4 - 返回
return [orderDescription copy];
注释2:得到一个包含 dictionary 中所有 key 的数组,然后使用 sortedArrayUsingComparator 这个 block 方法,把这些所有的 key 按升序进行排序。
3.enumerateKeysAndObjectsUsingBlock
这个方法苹果的官方说明:将一个 block 对象使用在一个字典的 entries 上(Apply an given block object to the entries of dictionary),我们来看一段实例代码:
-(float)totalOrder {
// 1 - 定义
__block float total = 0.0;
// 2 - 使用 block 计算
float (^itemTotal)(float,int) = ^float(float price, int quantity) {
return price *
// 3 - 使用 block 遍历 dictionary
[self.orderItems enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
IODItem* item = (IODItem*)
NSNumber* quantity = (NSNumber*)
int intQuantity = [quantity intValue];
total += itemTotal(item.price, intQuantity);
// 4 - 返回
注释1:需要注意的是 block,因为我们需要在 block 内部使用这个 total 变量;如果我们不使用 block 关键字,下面的 block 会创建一个这个 total 变量的常量拷贝(const copy),当指向 block 内部的时候,就使用这个拷贝。这意味着在 block 我们无法修改它的值。但添加了这个关键字我们就可以从 block 内部读取变量的值,或者把值写入 block 内的变量。
注释3:使用 enumerateKeysAndObjectsUsingBlock 可以遍历得到字典里所有的对象,以及它们对应的 key。
总结一些比较常用的 block
enumerateObjectsUsingBlock 这个是我最常使用的 block ,上面已经介绍过了,用来迭代数组非常方便,个人认为这应该是最好用的 block 了。
enumerateObjectsAtIndexes:usingBlock: 和 enumerateObjectsUsingBlock 差不多,但是我们可以选择只迭代数组的一部分,而不是迭代整个数组。这个需要迭代的范围由 indexSet 参数传入。
indexesOfObjectsPassingTest
返回一个数组中,通过了特定的 test 的对象的 indexSet。用这个 block 来查找特定的数据很方便。
NSDictionary
enumerateKeysAndObjectsUsingBlock 迭代整个字典,返回字典中所有的 key 和对应的值(如果是想用这个 block 来代替 objectForKey 方法,确实有些多此一举,但是如果你需要返回字典中全部的 key, value,这个 block 是一个很好的选择)。
keysOfEntriesPassingTest
和数组里的 indexesOfObjectsPassingTest
block 类似,返回通过特定的 test 的一组对象的 key。
UIView Animation
animateWithDuration: animation: completion: 写过动画大家应该还是比较了解的,动画的 block 实现确实比非 block 实现简单、方便了很多。
dispatch async:这时异步 GCD 的主要功能,在这里其实最重要的是要理解 block 是一个普通的对象,是可以作为参数传递给 dispatch queue 的。
使用我们自己的 block
除了使用这些系统提供给我们的 block,我们有时候自己写的方法里也许也想要用到 block。我们来看一些简单的示例代码,这段代码声明了一个接收 block 作为参数的方法:
-(void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.label.text = [NSString stringWithFormat:@"%d", mathBlock(3, 5)];
// 如何调用
-(IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a +
因为 block 就是一个 Objective-C 对象,所以我们可以把 block 存储在一个 property 中以便之后调用。这种方式在处理异步任务的时候特别有用,我们可以在一个异步任务完成之后存储一个 block,之后可以调用。下面是一段示例代码:
@property (strong) int (^mathBlock)(int, int);
// 存储 block 以便之后调用
-(void)doMathWithBlock:(int (^)(int, int))mathBlock {
self.mathBlock = mathB
// 调用上面的方法,并传入一个 block
-(IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a +
-(IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
还有,我们可以使用 typedef 来简化block 语法,当然效果和上面的是差不多的,我们来看下面的例子:
typedef int (^MathBlock)(int, int);
// 使用 tpyedef 声明一个property
@property (strong) MathBlock mathB
-(void)doMathWithBlock:(MathBlock) mathBlock {
self.mathBlock = mathB
-(IBAction)buttonTapped:(id)sender {
[self doMathWithBlock:^(int a, int b) {
return a +
-(IBAction)button2Tapped:(id)sender {
self.label.text = [NSString stringWithFormat:@"%d", self.mathBlock(3, 5)];
关于 block 的使用说的差不多了,其实关于 block 最关键的是要理解,block 是可以作为参数和返回值使用得。接下来会总结一篇关于 GCD 的文章。
1 收藏&&|&&32
你可能感兴趣的文章
2 收藏,627
17 收藏,1.1k
5 收藏,304
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
编辑预览的时候格式还是正确的,发布出来格式就有些错乱了,可能是 markdown 不太熟悉,抱歉!
编辑预览的时候格式还是正确的,发布出来格式就有些错乱了,可能是 markdown 不太熟悉,抱歉!
学习了!写的很棒
学习了!写的很棒
如何使用Block那一部分结尾你说“在上面的代码中, BOOL stop 设置为 YES 的时候,可以从block 内部停止下一步运行”可是关于block的代码并没有用到stop啊?[theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(@"The object at index %d is %@",idx,obj);
}];我想知道想用stop跳出怎么写?
如何使用Block那一部分结尾你说“在上面的代码中, BOOL stop 设置为 YES 的时候,可以从block 内部停止下一步运行”可是关于block的代码并没有用到stop啊?
[theArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
NSLog(@&The object at index %d is %@&,idx,obj);
我想知道想用stop跳出怎么写?
*stop = YES;
*stop = YES;
分享到微博?
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。如何在 iOS 5 中使用 Block_百度知道
如何在 iOS 5 中使用 Block
声明和使用Block:Apple文档中介绍了如何将一个Block声明为变量,并将其作为一个函数使用:int (^oneFrom)(int) = ^(int anInt) { return anInt - 1; }; // 创建了一个内联块^(int anInt)... ,其函数体和结果被传到了另外一个名为OneFrom的Block。 printf(&1 from 10 is %d&, oneFrom(10)); // 打印出: &1 from 10 is 9& // 这个block函数(distanceTraveled)传入3个float型参数,返回float值。 float (^distanceTraveled) (float, float, float) = ^(float startingSpeed, float acceleration, float time) { float distance = (startingSpeed * time) + (0.5 * acceleration * time * time);
}; 也可以传入一个Block作为一个参数,而不要以如上的方式声明,这样就可以在需要将block作为参数的时候以内联代码的方式简单地实现。NSArray *anArray = [NSArray arrayWithObjects: @&cat&, @&dog&,nil]; sortFunction(anArray, ^(string *a string *b){ if ( a == @&cat&) return TRUE; });
中国IT职业教育领先品牌
主营:专注UI,HTML5,PHP,JavaEE,Python,VR/AR,大数据开发培训。
int number2){ return number1+number2 }    如果我们将 Block 的声明和定义放在一起。 它将视图的透明度调整为 0,将这个视图向下和向右移动 50 点。 然后将这个 UIView 从它的 superview 中删除掉, 不过. 使用 Block 在执行速度上要比使用快速枚举快。 以 ^ 符号起始。  现在,让我们看看用快速枚举的方法实现同样的功能; }]..。 如果它里面有多条 return 语句;  for (id obj in theArray)  {  NSLog(@&quot,那这段代码你应该看起来很眼熟, 除了这个 ^ 符号;The object at index %d is %@&  [animatingView setAlpha:0];  [animatingView setCenter:CGPointMake(animatingView.center.x+50.0,animatingView.center.y+50.0)];  [UIView commitAnimations];  }  Block 的实现方式:  - (void)viewDidAppear:(BOOL)animated  { [super viewDidAppear:animated];  [UIView animateWithDuration:5.0 animations:^{  [animatingView setAlpha:0];  [animatingView setCenter:CGPointMake(animatingView.center.x+50.0, animatingView.center.y+50.0)];  } completion:^(BOOL finished) { [animatingView removeFromSuperview]; }];  }  如果我们仔细看一看这两个方法, 就会发现有 3 个优势:  更简单的代码 使用 Block, 我们不再需要单独定义一个回调方法, 或者调用 beginAnimations/commitAnimations 。  保持代码在一起 使用 Block, 我们不再需要在一个地方开启动画,然后再另外一个地方处理回调。 所有和我们动画相关的代码都在一处, 这样让他的可读性和维护性更强。  苹果推荐这样 这里有一个现实的例子, 苹果已经用 Block 重写了之前的一些功能, 现在苹果官方也推荐,如果可能的话,尽量迁移到基于 Block 的方法上面。  什么时候用 Blocks  我认为最佳的建议是, 在最合适用 Block 的地方使用它。 这里你可能会出于向后兼容或者更加熟悉以前的方式的原因,从而还要用老的方法。  但是每次你面临这种决策的时候, 想一想 Block 是否能让你更轻松以及你是否能用基于 Block 的方式代替现有代码。 然后选择一个对你最有价值的方法。  当然,你可能会发现,在以后的时间里, 你需要越来越多的使用 Block, 因为大多数框架,无论是第三方的还是苹果自己的,都正在用 Block 重写。, param_type:  BOOL stop:@selector(removeAnimationView)], 还必须和它要赋值到的 Block 声明中参数列表里面的参数类型和顺序相匹配。下面是实际的代码, .) { .. 使用 Block 我们可以不写任何附加的代码就可以访问对象,对象在数组中的索引,stop 变量。 这意味着少量的代码:animated]! ,并且可以继承它里面代码的返回值类型;  int idx = 0,并非我们一定会出现编码错误)。  速度; }  - (void)viewDidA Block Definition ^return_type(param_type param_name, param_type param_name:]  注意这里不需要参数的名称。    示例: NSArray  让我们看看 Block 如何改变我们操作数组的方式。  首先;,idx,obj):  简单性; [theArray count] ; i++)  {  NSLog(@&quot  return_type (^block_name)(param_type,减少了发生编码错误的机会(当然。 在我们这个例子中,你也可以加上它们。  下面是定义 Block 的一个例子:  int (^add)(int?  非 Block 的实现方式,这个优势就越来越重要。(来源)      示例:  ^(int number1; context:nil].)    如果你之前使用过其他 C 类型的语言:  int resultFromBlock = add(2;The object at index %d is %@&quot。  如果你能分析到 ^ 符号的意思 “我是一个 Block” 。  Block 还有另外一种不同的定义方法,让我们看一下一般情况下处理循环的方式:  BOOL  for (int i = 0 ; i &lt,如果你喜欢的话,那么祝贺你 – 你已经了解了 Block 中最难的部分, 我们会得到这样一个语句:  int (^add)(int,那么这些语句返回的类型必须都是相同的 (或者强制转换到相同的类型);,idx,obj):^(id obj, NSUInteger idx, BOOL *stop)  { NSLog(@&quot..,int) = ^(int number1, int number2){ return number1+number2; }  我们可以这样使用 B  [UIView setAnimationDidStopSThe object at index %d is %@&quot: UIView Animation  让我们对一个单独的 UIView 执行一个简单的动画, 很简单,对吗;  if (stop)    idx++;  }  现在,用 Block:  [theArray enumerateObjectsUsingB,i,[theArray objectAtIndex:i]);  if (stop)  }  上面方法中的 “stop” 变量,可能会让你不太明白。 但是如果用 Block 的方式实现它,你就会很清楚的看明白了。 Block 提供了一个 “stop” 变量能让你在任何时候停止循环:  //  [UIView beginAnimations,int)  下面是 Block 的定义格式;  [UIView setAnimationDuration:5.0], ;    在上面这个基于 Block 的代码中,你可能会好奇 “stop” 这个变量到底是什么。 这个变量可以在 block 中赋值为 YES, 这样就后续的任何循环都不会继续了。 这是传递到 enumerateObjectsUsingBlock 方法的 Block 中的其中一个参数,后面跟随着参数,这点微小的速度提升不值得一提,但是在更复杂的情况下。 ^ 这个符号表示了 “我们定义的这个东西是一个 Block”:@&Example&quot,这里的参数必须有参数名。  当你定义 Block 时, 返回值类型是可选的,2);  让我们看一看,我们简单的复制了这个功能来支持和 Block 的方式等同的效果.. return return_ }  这就是 Blcok 实际是怎么创建的:(BOOL)animated  {  [super viewDidAppear。  上面这个例子有些微不足道, 而且也很难明显的体现出 Block 所到来的好处。 但是我想给大家指出 Block 的两点好处,使用 Block 和不使用 Block 之间对比的一些例子:(id)sender  { [animatingView removeFromSuperview]:  - (void)removeAnimationView。  这里是 Block 定义的一个例子
本回答被网友采纳
为您推荐:
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。block使用小结、在arc中使用block、如何防止循环引用(zz)
zz from&http://www.cnbluebox.com/?p=255
使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试,发现竟然做错了几道,
才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客。
Block简介(copy一段)
Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理
要么导致return
cycle内存泄漏要么内存被提前释放导致crash。
Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅
实现函数的功能,还能携带函数的执行环境。
可以这样理解,Block其实包含两个部分内容
Block执行的代码,这是在编译的时候已经生成好的;
一个包含Block执行时需要的所有外部变量值的数据结构。
Block将使用到的、作用域附近到的变量的值建立一份快照拷贝到栈上。
Block与函数另一个不同是,Block类似ObjC的对象,可以使用自动释放池管理内存(但Block并不完全等同于ObjC对象,后面将详细说明)。
Block基本语法
基本语法在本文就不赘述了,同学们自学。
Block的类型与内存管理
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock,
NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
1、NSGlobalBlock如下,我们可以通过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock,可以当做函数使用。
&&&&//create
a NSGlobalBlock
(^sum)(float, float) =
^(float a, float b){
&&&&&&&&return
&&&&NSLog(@"block
is %@", sum); //block is
&__NSGlobalBlock__: 0x47d0&
2、NSStackBlock如下:
&&&&NSArray
*testArr = @[@"1", @"2"];
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"testArr
:%@", testArr);
&&&&NSLog(@"block
is %@", ^{
&&&&&&&&NSLog(@"test
Arr :%@", testArr);
&&&&//block
is &__NSStackBlock__: 0xbfffdac0&
&&&&//打印可看出block是一个
NSStackBlock, 即在栈上,
当函数返回时block将无效
&&&&NSLog(@"block
is %@", TestBlock);
&&&&//block
is &__NSMallocBlock__: 0x75425a0&
&&&&//上面这句在非arc中打印是
NSStackBlock, 但是在arc中就是NSMallocBlock
&&&&//即在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy.
3、NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取,但是retain操作就不行,会在下面说明
Block的copy、retain、release操作
(还是copy一段)
不同于NSObjec的copy、retain、release操作:
Block_copy与copy等效,Block_release与release等效;
对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
NSGlobalBlock:retain、copy、release操作都无效;
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry
addObject:stackBlock],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock
copy到堆上,然后加入数组:[mutableAarry
addObject:[[stackBlock copy]
autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。
Block对外部变量的存取管理
基本数据类型
1、局部变量
局部自动变量,在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。
base = 100;
(^sum)(int, int) = ^ long
(int a, int b) {
&&&&&&&&return
base + a +
&&&&printf("%ld\n",sum(1,2));
这里输出是103,而不是3,
因为块内base为拷贝的常量 100
2、STATIC修饰符的全局变量
因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量.
&&&&static
int base = 100;
(^sum)(int, int) = ^ long
(int a, int b) {
&&&&&&&&base++;
&&&&&&&&return
base + a +
&&&&printf("%ld\n",sum(1,2));
这里输出是4,而不是103,
因为base被设置为了0
&&&&printf("%d\n",
这里输出1,
因为sum中将base++了
3、__BLOCK修饰的变量
Block变量,被__block修饰的变量称作Block变量。
基本类型的Block变量等效于全局变量、或静态变量。
注:BLOCK被另一个BLOCK使用时,另一个BLOCK被COPY到堆上时,被使用的BLOCK也会被COPY。但作为参数的BLOCK是不会发生COPY的
block对于objc对象的内存管理较为复杂,这里要分static
global local
block变量分析、还要分非arc和arc分析
非ARC中的变量
先看一段代码(非arc)
@interface MyClass : NSObject
&&&&NSObject*
_instanceO
@implementation MyClass
NSObject* __globalObj =
- (id) init {
(self = [super
&&&&&&&&_instanceObj
= [[NSObject alloc]
&&&&return
- (void) test {
&&&&static
NSObject* __staticObj = nil;
&&&&__globalObj
= [[NSObject alloc]
&&&&__staticObj
= [[NSObject alloc]
&&&&NSObject*
localObj = [[NSObject alloc]
&&&&__block
NSObject* blockObj = [[NSObject
alloc] init];
&&&&typedef
void (^MyBlock)(void) ;
&&&&MyBlock
aBlock = ^{
&&&&&&&&NSLog(@"%@",
__globalObj);
&&&&&&&&NSLog(@"%@",
__staticObj);
&&&&&&&&NSLog(@"%@",
_instanceObj);
&&&&&&&&NSLog(@"%@",
localObj);
&&&&&&&&NSLog(@"%@",
blockObj);
&&&&aBlock
= [[aBlock copy]
autorelease];
&&&&aBlock();
&&&&NSLog(@"%d",
[__globalObj retainCount]);
&&&&NSLog(@"%d",
[__staticObj retainCount]);
&&&&NSLog(@"%d",
[_instanceObj retainCount]);
&&&&NSLog(@"%d",
[localObj retainCount]);
&&&&NSLog(@"%d",
[blockObj retainCount]);
int main(int argc, char
*argv[]) {
&&&&@autoreleasepool
&&&&&&&&MyClass*
obj = [[[MyClass alloc]
init] autorelease];
&&&&&&&&[obj
&&&&&&&&return
执行结果为1 1 1 2 1。
__globalObj和__staticObj在内存中的位置是确定的,所以Block
copy时不会retain对象。
_instanceObj在Block copy时也没有直接retain
_instanceObj对象本身,但会retain
self。所以在Block中可以直接读写_instanceObj变量。
localObj在Block
copy时,系统自动retain对象,增加其引用计数。
blockObj在Block
copy时也不会retain。
ARC中的变量测试
由于arc中没有retain,retainCount的概念。只有强引用和弱引用的概念。当一个变量没有__strong的指针指向它时,就会被系统释放。因此我们可以通过下面的代码来测试。
代码片段1(globalObject全局变量)
NSString *__globalString =
- (void)testGlobalObj
&&&&__globalString
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"string
is :%@", __globalString); //string is
http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&__globalString
&&&&TestBlock();
- (void)testStaticObj
&&&&static
NSString *__staticString =
&&&&__staticString
&&&&printf("static
address: %p\n",
&__staticString);&&&
//static address: 0x6a8c
(^TestBlock)(void) = ^{
&&&&&&&&printf("static
address: %p\n", &__staticString);
//static address: 0x6a8c
&&&&&&&&NSLog(@"string
is : %@", __staticString); //string is
http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&__staticString
&&&&TestBlock();
- (void)testLocalObj
&&&&NSString
*__localString = nil;
&&&&__localString
&&&&printf("local
address: %p\n",
&__localString); //local address:
0xbfffd9c0
(^TestBlock)(void) = ^{
&&&&&&&&printf("local
address: %p\n", &__localString);
//local address: 0x71723e4
&&&&&&&&NSLog(@"string
is : %@", __localString); //string is : 1
&&&&__localString
&&&&TestBlock();
- (void)testBlockObj
&&&&__block
NSString *_blockString = @"1";
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"string
is : %@", _blockString); // string is
http://www.cnbluebox.com/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&_blockString
&&&&TestBlock();
- (void)testWeakObj
&&&&NSString
*__localString = @"1";
&&&&__weak
NSString *weakString = __localS
&&&&printf("weak
address: %p\n",
&weakString);& //weak
address: 0xbfffd9c4
&&&&printf("weak
str address: %p\n", weakString); //weak str
address: 0x684c
(^TestBlock)(void) = ^{
&&&&&&&&printf("weak
address: %p\n", &weakString); //weak
address: 0x7144324
&&&&&&&&printf("weak
str address: %p\n", weakString); //weak str
address: 0x684c
&&&&&&&&NSLog(@"string
is : %@", weakString); //string is :1
&&&&__localString
&&&&TestBlock();
由以上几个测试我们可以得出:
1、只有在使用local变量时,block会复制指针,且强引用指针指向的对象一次。其它如全局变量、static变量、block变量等,block不会拷贝指针,只会强引用指针指向的对象一次。
2、即时标记了为__weak或__unsafe_unretained的local变量。block仍会强引用指针对象一次。(这个不太明白,因为这种写法可在后面避免循环引用的问题)
循环引用retain
循环引用指两个对象相互强引用了对方,即retain了对方,从而导致谁也释放不了谁的内存泄露问题。如声明一个delegate时一般用assign而不能用retain或strong,因为你一旦那么做了,很大可能引起循环引用。在以往的项目中,我几次用动态内存检查发现了循环引用导致的内存泄露。
这里讲的是block的循环引用问题,因为block在拷贝到堆上的时候,会retain其引用的外部变量,那么如果block中如果引用了他的宿主对象,那很有可能引起循环引用,如:
self.myblock = ^{
&&&&&&&&&&&&[self
doSomething];
&&&&&&&&};
为测试循环引用,写了些测试代码用于避免循环引用的方法,如下,(只有arc的,懒得做非arc测试了)
- (void)dealloc
&&&&NSLog(@"no
cycle retain");
- (id)init
= [super init];
#if TestCycleRetainCase1
&&&&&&&&//会循环引用
&&&&&&&&self.myblock
&&&&&&&&&&&&[self
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase2
&&&&&&&&//会循环引用
&&&&&&&&__block
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase3
&&&&&&&&//不会循环引用
&&&&&&&&__weak
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase4
&&&&&&&&//不会循环引用
&&&&&&&&__unsafe_unretained
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
&&&&&&&&NSLog(@"myblock
is %@", self.myblock);
&&&&return
- (void)doSomething
&&&&NSLog(@"do
Something");
int main(int argc, char
*argv[]) {
&&&&@autoreleasepool
&&&&&&&&TestCycleRetain*
obj = [[TestCycleRetain alloc]
&&&&&&&&obj
&&&&&&&&return
经过上面的测试发现,在加了__weak和__unsafe_unretained的变量引入后,TestCycleRetain方法可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。
因此防止循环引用的方法如下:
__unsafe_unretained TestCycleRetain *weakSelf =
In manual reference counting
mode,&__block
the effect of not retaining&x.
In ARC mode,&__block
to retaining&x&(just
like all other values). To get the manual reference counting mode
behavior under ARC, you could
use&__unsafe_unretained
As the name&__unsafe_unretained&implies,
however, having a non-retained variable is dangerous (because it
can dangle) and is therefore discouraged. Two better options are to
either use&__weak&(if
you don’t need to support iOS&4 or
OS&X&v10.6), or set
the&__block&value
break the retain cycle.
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 ios block使用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信