自IOS4.0起加入Block语法,UIKit中超过一百个API使用了Block,主要用在线性遍历(简化代码,更好的抽象),方法回调(相比使用delegate模块化程度更高),异步逻辑(GCD框架下)。
在一些开源的项目里面也都提供支持Block语法的API,如ASIHttpRequest、Reachability等,更有专门扩展现有框架提供Block支持的Blockskit项目。很显然Block对于追求优雅是有很大帮助的。
关于Block实现原理,可以参看:http://blog.youkuaiyun.com/jasonblog/article/details/7756763 ,
关于递归和泛型的:http://blog.youkuaiyun.com/onlyou930/article/details/6999063,
在此不再累赘说明。
线性遍历
苹果官方例1:没有for循环的代码很美观嘛,Block内部可以使用作用域内的自由变量,申明为__block则会以传引用的方式使用
__block BOOL found = NO;
NSSet *aSet = [NSSet setWithObjects: @"Alpha", @"Beta", @"Gamma", @"X", nil];
NSString *string = @"gamma";
[aSet enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
if ([obj localizedCaseInsensitiveCompare:string] == NSOrderedSame) {
*stop = YES;
found = YES;
}
}];
苹果官方例2:可以看到compareStringsBlock是可以被很好的复用的
NSComperator compareStringsBlock = ^(id stringA, id stringB) {
NSRange rangeS = NSMakeRange(0, [stringA length]);
return (stringA compare:stringB options:comparisonOptions range:rangeS locale:currentLocale];
};
NSArray *compareSortArray = [arrayOfStringDays sortArrayUsingComparator:compareStringsBlock]);
方法回调
示例1:BlocksKit中对NSTimer的扩展,免去了在另外一个地方写个回调函数的麻烦。
// 启动计时操作
__block int _total = 0;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 block:^(NSTimeInterval time) {
_total += 1;
NSLog(@"total is %d", _total);
// 这里更新UI界面 显示时间
} repeats:YES];
示例2:ASIHttpRequest中的例子,这里request要申明为__block是防止循环引用
- (IBAction)grabURLInBackground:(id)sender
{
NSURL *url = [NSURL URLWithString:@"http://allseeing-i.com"];
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setCompletionBlock:^{
// Use when fetching text data
NSString *responseString = [request responseString];
// Use when fetching binary data
NSData *responseData = [request responseData];
}];
[request setFailedBlock:^{
NSError *error = [request error];
}];
[request startAsynchronous];
}
GCD应用
示例1:方便的创建异步逻辑,并在异步逻辑执行完之后到主线程更新UI
[request setCompletionBlock:^{
NSData *responseData = [request responseData];
// 在并发任务队列(非主线程的一个线程池)解包数据并写入数据库
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UniPacket *uniPacket = [UniPacket fromData:responseData];
// 此处写入数据库,去除重复数据 并整理要显示的数据_finalDatas
dispatch_async(dispatch_get_main_queue(), ^{
// 在此处将finalDatas在主线程中更新到UI界面
}
});
}];
作用域问题
因为Block是分配在栈上面的C语言对象,所以要注意其作用域范围,下面的代码block调用是不正确的
同时我们也可以知道,如果要将Block作为函数参数传递的话,或者要异步使用的话,需要先将Block复制为堆上的对象
对于Block里面使用到的上下文对象,Block对象会将其retain一份,使用self访问成员变量和使用成员变量名访问,ratain的对象是不一样的,前者retain的是self,后者retain的是成员变量当前指向的对象
dispatch_block_t block;
if (x) {
block = ^{ printf("true\n"); };
} else {
block = ^{ printf("false\n"); };
}
block();
单例模式
既然扯远了,就再扯远点,下面是苹果公司引入的适合创建单例的一种方式
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题
+ (ClassnName *)sharedInstance
{
static ClassnName *obj = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
obj = [[ClassnName alloc] init];
});
return obj;
}