目标C:有没有办法在另一个方法中调用一个方法的完成块?

Posted

技术标签:

【中文标题】目标C:有没有办法在另一个方法中调用一个方法的完成块?【英文标题】:Objective C: Is there a way to call a completion block for a method in another method? 【发布时间】:2018-11-27 18:26:54 【问题描述】:

我有多个GET API 请求方法,它们在完成时调用完成块。这是一个例子。

- (void)getUserInfo
    onSuccess:(void (^)(id))successBlock
    onFailure:(void (^)(NSError *))failureBlock 

    NSString *urlStr = [NSString stringWithFormat:@"%@/user/", baseUrl];

    [manager GET:urlStr parameters:nil progress:nil
          success:^(NSURLSessionTask *task, id responseObject) 
              successBlock(responseObject);
          
          failure:^(NSURLSessionTask *operation, NSError *error) 
              failureBlock(error);
          ];

但是,我注意到我在其他方法中重复了经理GET 请求代码。我想创建另一种处理所有请求并删除重复代码的方法。 URL 似乎是唯一改变的东西。但是,有一个缺陷。我需要调用successBlock 让方法知道请求已完成。

也许我需要完全走另一条路,做一些不同的事情。

【问题讨论】:

【参考方案1】:

您可以传递完成块,然后从处理所有获取请求的最终方法中调用它们。为简洁起见,我通常制作将被重用的 typedef 的完成块。这是我的意思的一个例子(我添加了第二个示例方法,它也传递到中心getRequestWithURLString:onSuccess:onFailure: 方法):

LLFakeManager.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef void (^_Nullable SuccessCompletionBlock)(id responseObject);
typedef void (^_Nullable FailureCompletionBlock)(NSError *error);

@interface LLFakeManager : NSObject
- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock;
@end

NS_ASSUME_NONNULL_END

LLFakeManager.m

#import "LLFakeManager.h"

@interface LLFakeManager()
- (void)getRequestWithURLString:(NSString *)urlString
                      onSuccess:(SuccessCompletionBlock)successBlock
                      onFailure:(FailureCompletionBlock)failureBlock;
@end

@implementation LLFakeManager

- (void)getUserInfoOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock 
    NSString *urlStr = @"FakeUserUrlPath";
    [self getRequestWithURLString:urlStr onSuccess:successBlock onFailure:failureBlock];


- (void)getBooksCheckedOutOnSuccess:(SuccessCompletionBlock)successBlock onFailure:(FailureCompletionBlock)failureBlock 
    NSString *urlString = @"FakeBooksUrlPath";
    [self getRequestWithURLString:urlString onSuccess:successBlock onFailure:failureBlock];


// central method that will handle all the get requests
- (void)getRequestWithURLString:(NSString *)urlString
                      onSuccess:(SuccessCompletionBlock)successBlock
                      onFailure:(FailureCompletionBlock)failureBlock 
    // some fake implementation here to do your request, then use the completion block passed in from whatever other method
    if (successBlock) 
        successBlock(@"responseObjectPassedBackHere");
    


@end

还有一个调用它的例子:

LLFakeManager *manager = [[LLFakeManager alloc] init];
[manager getUserInfoOnSuccess:^(id  _Nonnull responseObject) 
    NSLog(@"Here's my response object = %@", responseObject);
 onFailure:^(NSError * _Nonnull error) 
    // no implementation but same idea
];

会产生这个日志:

Here's my response object = responseObjectPassedBackHere

此站点:http://goshdarnblocksyntax.com 是一个方便的块语法列表,可能对您也有帮助。

【讨论】:

谢谢,但我有一个问题。这个 NS_ASSUME_NONNULL_BEGIN 有什么意义 Xcode 应该在创建新类时默认添加它,这只是快速操作,因此您不必为每个属性/方法添加可空性说明符——这基本上意味着一切都假定具有默认值_Nonnull/nonnull 的说明符,除非您另外指定使用 _Nullable/nullable Nullability 说明符有助于捕获当 nil 传递给方法时您没有正确处理场景的情况,并允许 Xcode 在您可能将 nil 传递给一个方法时警告您需要一个非空参数。在 Xcode 中,当您使用 Product Menu > Analyze 时,分析器应该会拾取这些 好的,我有一些可以接受 nil 的参数,因为它是可选的。我应该用 _Nullable 标记它们吗?感谢您的快速响应。 是的,正如您从我的示例代码中看到的那样,我已将完成块标记为 _Nullable,因为在某些情况下它们可能未被使用,但也要注意在中心方法中我正在检查当调用者传入 nil get 时,成功块的存在来处理场景。即 if (successBlock)【参考方案2】:

这些块——如果它们具有相同的签名——它们可以沿着一个方法链传递。您的 GET 块带有不必要的第一个参数。 NSURLSessionTask *,如果要返回,应该同步返回。将其移出区块签名将使您能够标准化区块。

在代码中更容易说...

// changed this method name so it would compile

- (void)getUserInfoOnSuccess:(void (^)(id))successBlock
                   onFailure:(void (^)(NSError *))failureBlock 

    NSString *urlStr = [NSString stringWithFormat:@"%@/user/", baseUrl];

   // two things: get the task as a return value (if you need it)
   // pass the blocks directly, without nesting them in new blocks
    NSURLSessionTask *task = [manager GET: urlStr
                               parameters: nil
                                 progress: nil
                                  success: successBlock
                                  failure: failureBlock];
    // do something with the task

要完成这项工作,请更改 GET 方法的返回类型和块签名...

- (NSURLSessionTask *)GET:(NSString *)url parameters:(id)params progress:(id)progress success:(void (^)(id))successBlock failure:(void (^)(NSError *))failureBlock 
    // return the session task created here
    return task

【讨论】:

以上是关于目标C:有没有办法在另一个方法中调用一个方法的完成块?的主要内容,如果未能解决你的问题,请参考以下文章

没有调用目标 C 完成块

一个方法中的hashMap的值,怎么在另一个类中调用啊?

xctest 如何在另一种测试方法中使用在一种测试方法中捕获的数据

在目标c中使用完成块完成方法后如何执行另一种方法?

在另一个类c ++中调用成员函数

在目标 C 中的单独线程上延迟方法