Objective-C中的块是否真的有用?它们的用途是什么?

Bax*_*pez 2 objective-c objective-c-blocks

我刚刚读到了关于块的内容,我知道它们只是将信息封装为普通方法,但具有自己强大的引用数据.我想知道块的好用是什么?

aop*_*fan 11

这是用于我的项目的块的用途; 替换代表和协议(在某些情况下).

问题

假设您需要从服务器异步加载数据.您可能有一个方法需要PUT到一个路径(带有数据),然后最终,当任务完成时,将结果发送给方法调用者.

代表和协议解决方案

这是我们客户的方法签名,称之为AppClient:

- (void)putToPath:(NSString *)path withData:(id)data;
Run Code Online (Sandbox Code Playgroud)

我们不能在这个方法的返回中包含数据,因为它是异步的(这意味着它不会等待任务完成其他事情,比如运行下一行代码).相反,我们构建一个协议:

@protocol AppClientRequestDelegate
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData;
@end
Run Code Online (Sandbox Code Playgroud)

然后你的AppClient类会创建一个这样的属性:

@property (weak, nonatomic)id<AppClientRequestDelegate> requestDelegate;
Run Code Online (Sandbox Code Playgroud)

putToPath...方法的调用者将其AppClient requestDelegate属性的实例设置为self,并实现该方法,然后使用pathsentData参数验证正确的请求,并对参数执行某些操作或其他操作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
    }
}
Run Code Online (Sandbox Code Playgroud)

这一切都很棒,但是当你有一堆PUT请求到同一条路径时,它会很糟糕,并试图区分协议实现中的请求.我猜你可以在委托方法和putToPath...为每个请求指定id 的方法中添加另一个参数,但这会是混乱和混乱.

另一个潜在的问题是如果你在这个应用程序中广泛使用异步加载 这可能导致很多代表和协议.

块解决方案

我们将方法签名扩展为包含一个块:

- (void)putToPath:(NSString *)path withData:(id)data completion:(void (^)(id returnedData))completion;
Run Code Online (Sandbox Code Playgroud)

当然,这种语法非常令人生畏,但它不仅包括来自协议的所有信息,而且允许方法的调用者将所有逻辑压缩到一个方法中,从而将在该方法中调用的局部变量带入块的范围内.实现.

我们的调用者代码现在看起来像这样:

- (void)syncData:(id)data {
    [self.appClient putToPath:@"/posts/9" withData:data completion:^(id returnedData) {
        // Do something with returnedData
    }];
}
Run Code Online (Sandbox Code Playgroud)

结论

你要求使用积木,我相信这是一个非常好的; 它可能不适用于您,但您可以看到它不仅减少了代码质量,而且使其更具可读性和健壮性.