打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
iOS之Block代码块的定义及使用
个人觉得Block优势如下:第一可以使代码看起来更简单明了,第二可以取代以前的delegate使代码的逻辑看起来更清晰。
借一张图表达基本定义:
Block在定义时并不会执行内部的代码,只有在调用时候才会执行。
[objc]
使用示例:
// 在AAViewController.h定义
@property (nonatomic, copy) void (^successBlock)(NSInteger count);
// 在AAViewController.m赋值
if (self.successBlock && !_willUpdate){
self.successBlock([self.cards count]);
}
在BViewController.m中调用:
AAViewController *aa=[[[AAViewController alloc] init];
// 回调要如何处理
aa.successBlock=^(NSInteger count) {
if (count==0) {
// 处理代码
}
};
[aa httpRequest];
利用typedef为Block进行重命名
我们可以使用typedef为block进行一次重命名,方法跟为函数指针进行重命名是一样的:
[objc]
typedef int (^Sum) (int,  int);
这样我们就利用typedef定义了一个block,这个block的名字就是Sum,需要传入两个参数。当我们需要使用时,就可以这样做了:
[objc]
Sum mysum = ^(int a, int b) {
n = 2;
return (a + b)*n;
};
这样就完整的定义好了一个block了,接下来的使用如下:
[objc]
#import <Foundation/Foundation.h>
typedef int (^Sum) (int, int);
int main(int argc, const charchar * argv[])
{
__block int n = 1;
@autoreleasepool {
Sum mysum = ^(int a, int b) {
n = 2;
return (a + b)*n;
};
NSLog(@"(3 + 5) * %i = %d", n, mysum(3, 5));
}
return 0;
}
Block在内存中的位置
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
[objc]
示例1:
BlkSum blk1 = ^ long (int a, int b) {
return a + b;
};
NSLog(@"blk1 = %@", blk1);// 打印结果:blk1 = <__NSGlobalBlock__: 0x47d0>
示例2:
int base = 100;
BlkSum blk2 = ^ long (int a, int b) {
return base + a + b;
};
NSLog(@"blk2 = %@", blk2); // 打印结果:blk2 = <__NSStackBlock__: 0xbfffddf8>
示例3:
BlkSum blk3 = [[blk2 copy] autorelease];
NSLog(@"blk3 = %@", blk3); // 打印结果:blk3 = <__NSMallocBlock__: 0x902fda0>
blk1和blk2的区别在于:
blk1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使blk1与一般函数没有任何区别。
blk2与blk1唯一不同是的使用了局部变量base,在定义(注意是“定义”,不是“运行”)blk2时,局部变量base当前值被copy到栈上,作为常量供Block使用。执行下面代码,结果是203,而不是204。
[objc]
int base = 100;
base += 100;
BlkSum sum = ^ long (int a, int b) {
return base + a + b;
};
base++;
printf("%ld",sum(1,2));
在Block内变量base是只读的,如果想在Block内改变base的值,在定义base时要用 __block修饰:__block int base = 100;
[objc]
__block int base = 100;
base += 100;
BlkSum sum = ^ long (int a, int b) {
base += 10;
return base + a + b;
};
base++;
printf("%ld\n",sum(1,2));
printf("%d\n",base);
输出将是214,211。Block中使用__block修饰的变量时,将取变量此刻运行时的值,而不是定义时的快照。这个例子中,执行sum(1,2)时,base将取base++之后的值,也就是201,再执行Blockbase+=10; base+a+b,运行结果是214。执行完Block时,base已经变成211了。
局部自动变量:在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。
[objc]
int base = 100;
BlkSum sum = ^ long (int a, int b) {
// base++; 编译错误,只读
return base + a + b;
};
base = 0;
printf("%ld\n",sum(1,2)); // 这里输出是103,而不是3
static变量、全局变量。如果把上个例子的base改成全局的、或static。Block就可以对他进行读写了。因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量。
[objc]
static int base = 100;
BlkSum sum = ^ long (int a, int b) {
base++;
return base + a + b;
};
base = 0;
printf("%d\n", base);
printf("%ld\n",sum(1,2)); // 这里输出是3,而不是103
printf("%d\n", base);
输出结果是0 4 1,表明Block外部对base的更新会影响Block中的base的取值,同样Block对base的更新也会影响Block外部的base值。
Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
retain cycle
retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。比如:
[objc]
@implementation TsetBlock
-(id)init{
if (self = [superinit]) {
self.testStr =@"中国";
self.block = ^(NSString *name, NSString *str){
NSLog(@"arr:%@",self.testStr); // 编译警告:Capturing 'self' strongly in this block is likely to lead to a retain cycle
};
}
returnself;
}
@end
网上大部分帖子都表述为"block里面引用了self导致循环引用",但事实真的是如此吗?我表示怀疑,其实这种说法是不严谨的,不一定要显式地出现"self"字眼才会引起循环引用。我们改一下代码,不通过属性self.testStr去访问String变量,而是通过实例变量_testStr去访问,如下:
[objc]
@implementation TsetBlock
-(id)init{
if (self = [superinit]) {
self.testStr =@"中国";
self.block = ^(NSString *name,NSString *str){
NSLog(@"arr:%@", _testStr); // 同样出现: Capturing 'self' strongly in this block is likely to lead to a retain cycle
};
}
returnself;
}
@end
可以发现:
即使在你的block代码中没有显式地出现"self",也会出现循环引用!只要你在block里用到了self所拥有的东西!
要分两种环境去解决:在ARC下不用__block ,而是用 __weak 为了避免出现循环引用
1.ARC:用__week
__weaktypeof (self)  weakSelf = self; 或者
__weak someClass *weakSelf = self;
2.MRC:用__block ,__block修饰的变量在Block copy时是不会retain的,所以,也可以做到破解循环引用。
__block someClass *blockSelf = self;
Block的copy、retain、release操作
对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
NSGlobalBlock:retain、copy、release操作都无效;
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[[mutableAarry addObject:stackBlock],在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。
几个应用实例:
1、代码用在字符串数组排序
NSArray *stringArray = [NSArray arrayWithObjects:@"abc 1", @"abc 21", @"abc 12",@"abc 13",@"abc 05",nil];
NSComparator sortBlock = ^(id string1, id string2)
{
return [string1 compare:string2];
};
NSArray *sortArray = [stringArray sortedArrayUsingComparator:sortBlock];
NSLog(@"sortArray:%@", sortArray);
运行结果:sortArray:(
"abc 05",
"abc 1",
"abc 12",
"abc 13",
"abc 21"
)
2、代码块的递归调用
代码块想要递归调用,代码块变量必须是全局变量或者是静态变量,这样在程序启动的时候代码块变量就初始化了,可以递归调用
static void (^ const blocks)(int) = ^(int i)
{
if (i > 0) {
NSLog(@"num:%d", i);
blocks(i - 1);
}
};
blocks(3);
运行打印结果:
num:3
num:2
num:1
参考资料源自互联网
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
正确使用Block避免Cycle Retain和Crash
关于Block的copy和循环引用的问题
Objc中Block的原理、实现及注意事项
Delegate & Block
iOS 使用Block
iOS: ARC和非ARC下使用Block属性的问题
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服