block使用技巧

来源:互联网 发布:数据库 论坛回复 编辑:程序博客网 时间:2024/05/16 06:58

1.为常用的块类型创建typedef

typedef return_type (^block_type) (parameters);
block_type是块的类型名

typedefint(^EOCCompletionHandler(NSData*data, NSTimeInterval duration,NSError*error);  //为块创建typedef
- (void)startWithCompletionHandler:(EOCCompletionHandler) completion;

好处:
1.更加清晰
2.方便重构块的类型签名,比如说,要给原来的completion handler块再加一个参数,用以表示完成任务所花时间,那么只需修改类型定义语句即可:
typedefint(^EOCCompletionHandler) (NSData*data, NSTimeInterval duration,NSError*error);
修改之后,凡是使用了这个类型定义的地方,如方法签名等处,都会无法编译,于是可以逐个修改。

2.用途不同时应定义不同的块。

方法签名是ObjC中对一个方法的参数类型和返回值类型的一条记录。每个方法都对应一个方法签名。

typedefvoid(^ACAcountStoreSaveCompletionHandler) (BOOLsuccess, NSError *error);
typedefvoid(^ACAcountStoreRequestCompletionHandler) (BOOLsuccess, NSError *error);

这两个类型定义的签名相同,可以合并成一个typedef,然而,这么做之后,块与参数的用途看上去就不那么明显了。

与此相似,如果有好几个类都要执行相似但各有区别的异步任务,而这几个类又不能放入同一个继承体系,那么,每个类就应该有自己的completion handler类型。

3.使用委托和块实现回调对比

3.1使用委托

令关注此事件的对象遵从该协议。对象成为delegate之后,就可以在相关事件发生时得到通知了。
#import<Foundation/Foundation.h>
@class EOCNetworkFetcher;

@protocol EOCNetworkFetcherDelegate <NSObject>
- (
void)networkFetcher:(EOCNetworkFetcher*)fetcher didReceiveData:(NSData*)data;
- (
void)networkFetcher:(EOCNetworkFetcher*)fetcher didFailWithError:(NSError*)error;
@end

@interface EOCNetworkFetcher : NSObject
@property (nonatomic,weak)id<EOCNetworkFetcherDelegate> delegate;
- (
id)initWithURL:(NSURL*)url;
- (
void)start;
@end

其他类则可像下面这样使用此类提供的API:
- (void)fetchFooData {
   
NSURL *url = [[NSURLalloc]initWithString:@"http://www.example.com/foo.dat"];
   
EOCNetworkFetcher*fetcher = [[EOCNetworkFetcheralloc]initWithURL:url];
    fetcher.
delegate= self;
    [fetcher
start];
}

- (
void)networkFetcher:(EOCNetworkFetcher*)fetcher didReceiveData:(NSData*)data {
    _fetchedFooData = data;
}
这种做法确实可行,然而如果改用块来实现,代码会更加清晰。但委托模式有个缺点:如果类要分别使用多个获取器下载不同的数据,那么就得在delegate回调方法里根据传入的获取器参数来切换。
- (void)networkFetcher:(EOCNetworkFetcher*)fetcher didReceiveData:(NSData*)data {
   
if(fetcher == _fooFetcher) {
        _fetchedFooData = data;
        _fooFetcher =
nil;
    }
else if(fetcher == _barFetcher) {
        _fetchedBarData = data;
        _barFetcher =
nil;
    }
}

3.2使用block

#import<Foundation/Foundation.h>
typedefvoid(^EOCNetworkFetcherCompletionHandler)(NSData*data);

@interface EOCNetworkFetcher : NSObject
@property (nonatomic,weak)id<EOCNetworkFetcherDelegate> delegate;
- (
id)initWithURL:(NSURL*)url;
- (
void)startWithCompletionHandler:(EOCNetworkFetcherCompletionHandler)handler;
@end

- (void)fetchFooData {
   
NSURL *url = [[NSURLalloc]initWithString:@"http://www.example.com/foo.dat"];
   
EOCNetworkFetcher*fetcher = [[EOCNetworkFetcheralloc]initWithURL:url];
    [fetcher
startWithCompletionHandler:^(NSData*data) {
        _fetchedFooData = data;
    }];
}
与使用委托模式的代码相比,用块写出来的代码显然更为整洁。异步任务执行完后所需运行的业务逻辑,和启动异步任务所用的代码放在一起了。而且,由于块声明在创建获取器范围里,所以他可以访问此范围内的全部变量。而使用委托模式需要把这些变量作为参数传入。
并且如果有多个获取器也不需要根据获取器参数来切换处理业务逻辑,每个completion handler的业务逻辑,都是和相关的获取器对象一起定义:
- (void)fetchFooData {
   
NSURL *url = [[NSURLalloc]initWithString:@"http://www.example.com/foo.dat"];
   
EOCNetworkFetcher*fetcher = [[EOCNetworkFetcheralloc]initWithURL:url];
    [fetcher
startWithCompletionHandler:^(NSData*data) {
        _fetchedFooData = data;
    }];
}

- (
void)fetchBarData {
   
NSURL *url = [[NSURLalloc]initWithString:@"http://www.example.com/foo.dat"];
   
EOCNetworkFetcher*fetcher = [[EOCNetworkFetcheralloc]initWithURL:url];
    [fetcher
startWithCompletionHandler:^(NSData*data) {
        _fetchedBarData = data;
    }];
}

总结:委托和block都可以实现回调,block能把异步任务执行完后所需运行的业务逻辑,和启动异步任务所用的代码放在一起,代码更加清晰。委托模式有一个缺点:如果类要分别使用多个网络获取器下载不同的数据,任务完成后,那么就得在delegate回调方法里根据传入的获取器参数来切换。 
0 0