您的位置:首页 > 移动开发 > IOS开发

对iOS中block的一点领悟

2014-03-26 15:13 211 查看
看之前可以先做一下这个测试题目,如果都不能难倒你,那么下面的你也不用看了。

在Objective-C语言中,一共有3种类型的block:
_NSConcreteGlobalBlock 全局的静态block,不会访问任何外部变量。
_NSConcreteStackBlock 保存在栈中的block,当函数返回时会被销毁。
_NSConcreteMallocBlock 保存在堆中的block,当引用计数为0时会被销毁。
一、全局静态的block和C函数是一样的,位于代码段中,其特征就是么有引用任何外部变量
{
//create a NSGlobalBlock
float (^sum)(float, float) = ^(float a, float b){

return a + b;
};

NSLog(@"block is %@", sum); //block is <__NSGlobalBlock__: 0x47d0>
}


二、保存在栈中的block,block函数返回时被销毁,其引用的外部变量会被copy到栈上去,所以在这一类型block中引用了外部变量是无法改变外部变量的,当然我们可以使用__block来修饰你想要改变的外部变量以告知编译器“我需要在block内部修改这个外部变量”,这样编译器就会在block内部直接使用该变量的引用而不是再copy一份。
{
NSArray *testArr = @[@"1", @"2"];

void (^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.
}


此外保存在栈中的block对于arc和非arc来说有一点的区别,arc中默认会将栈中的block拷贝到堆中来(其原因本也不是很清楚,但是从下面提到的一些参考资料中很可能是因为arc中默认是strong的引用类型,那么将默认把block拷贝到堆上由arc来管理其内存),来看下面一个例子:
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{
printf("%c\n", b);
}];
}

void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
block();
}
由于该block引用了外面变量“b”,所以这个block的应该是保存在栈上的。在来看看它在arc和非arc中区别:
首先在非arc中,它是一个栈block所以它会在函数返回是销毁,那么在addObject的时候,此block已经被销毁了;
在arc中,它会默认被copy到堆中来成为一个堆block,函数返回不会立即被销毁,将由arc来管理其内存,所以以上代码是正确可行的。

三、保存在堆的block,我们可以通过copy栈block来得到堆block,C系列的程序员应该都知道堆上的内存是由我们自己管理的,所以在非arc中需要自己管理其内存,而在arc中我们只需要交给arc去管理就好了。

此外使用block有一点是需要特别注意的就是防止循环引用:
@property (nonatomic, copy)TestBlock block;

self.block = ^{
//arc中会编译会发出有循环饮用风险的警告
[self doSomething];
};

self.block();


如上:self持有myblock,在block中又引用了self,block在被copy到堆上的时候会retain其引用的外部变量,这样myblock也持有了self,他们相互持有就造成了循环引用,谁都无法释放。
arc中正确的做法是:
__weak testBViewController* weakSelf = self;

self.block = ^{
//arc中会编译会发出有循环饮用风险的警告
[weakSelf doSomething];
};

self.block();


非arc中正确的做法是:
self.block = ^{
//arc中会编译会发出有循环饮用风险的警告
[weakSelf doSomething];
};

self.block();
self.block = nil;

当然在arc中也可以加上“self.block = nil”这句来达到释放block的目的,但是这样的做法是不推荐的。

以上领悟主要参考了一下blog
唐巧boy的blog(知名iOS开发大牛)

http://blog.sina.com.cn/s/blog_8c87ba3b0101m599.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: