这是适用于我的项目的块的用途;替换代表和协议(在某些情况下)。
问题
假设您需要从服务器异步加载数据。您可能有一个方法需要 PUT 到路径(带有数据),然后最终在任务完成时将结果发送给方法调用者。
委托和协议解决方案
这是我们客户的方法签名,调用它AppClient
:
- (void)putToPath:(NSString *)path withData:(id)data;
我们不能在该方法的返回中包含数据,因为它是异步的(意味着它不会等待任务完成来执行其他操作,例如运行下一行代码)。相反,我们构建一个协议:
@protocol AppClientRequestDelegate
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData;
@end
那么你的AppClient
类将创建一个如下所示的属性:
@property (weak, nonatomic)id<AppClientRequestDelegate> requestDelegate;
的来电者putToPath...
方法将设置他的 AppClient 实例requestDelegate
财产给self
,并实现该方法,然后使用path
and sentData
参数,并用receivedData
范围。
我们的调用者代码如下所示:
- (void)syncData:(id)data {
[self.appClient putPath:@"/posts/9" withData:data];
}
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData {
if (/*path and sentData are right*/) {
// Do something with recievedData
}
}
这一切都很棒,但是当您向同一路径发出一堆 PUT 请求并尝试区分协议实现中的请求时,情况就很糟糕了。我想您可以向委托方法和putToPath...
方法为每个请求指定一个 id,但这会很混乱且令人困惑。
另一个潜在的问题是您是否在整个应用程序中广泛使用异步加载;这可能会产生大量的代表和协议。
块解决方案
我们扩展我们的方法签名以包含一个块:
- (void)putToPath:(NSString *)path withData:(id)data completion:(void (^)(id returnedData))completion;
诚然,这种语法相当令人畏惧,但它不仅包含协议中的所有信息,而且允许方法的调用者将所有逻辑压缩到一个方法中,从而将该方法中调用的局部变量带入块的范围中。执行。
我们的调用者代码现在如下所示:
- (void)syncData:(id)data {
[self.appClient putToPath:@"/posts/9" withData:data completion:^(id returnedData) {
// Do something with returnedData
}];
}
结论
你要求很好地使用块,我相信这是一个非常好的使用;它可能不适用于您,但您可以看到它不仅减少了代码量,而且还使其更具可读性和健壮性。