繁体   English   中英

从块内部进行回调(目标C)

[英]Callback from inside a block (Objective C)

我有一个带有块的方法,我希望它在存在时立即将userID发送到另一个方法。 userID是从Internet解析的值,因此加载和“存在”通常需要2秒钟左右。 有什么方法可以将userID存在时,将其发送给另一种方法?
这是我所有的代码:

- (void)parseForUserID {
    //Get the Data you need to parse for (i.e. user main page returned as a block of NSData.
    TClient *client = [[TClient alloc] init];
    [client loginToMistarWithPin:@"20014204" password:@"yuiop" success:^{
        [client getUserID:^(NSString *result) {
            NSString *userIDWithHTML = [self userIDRegex:result];
            NSString *userID = [self onlyNumbersRegex:userIDWithHTML];

            //if userID exists, send it to another method in a different class

        }];
    } failure:^{
        NSLog(@"login failed from controller");
    }];
}

我看到这是您提出的与同一问题相关的第三个问题,因此我想您在理解块时遇到了一些麻烦。

首先,您必须了解该块在某种意义上可以看作是一个函数。 区别在于,与函数不同,该块没有名称,并且无需使用函数名称,而只是将代码内联在需要的位置。

要了解的第二件事是,块通常用作回调 其他回调机制是函数指针和委托。 当您将块作为参数传递给函数时,您基本上是在告诉函数: “嘿,当满足某些条件时,请为我执行这个小代码”

第三想理解的是,该块(或任何回调)是否将被同步调用。 其实这无关与块本身, 本身 ,而是与被调用的函数。 如果该函数是异步的,则该函数将创建另一个线程并立即返回以执行调用异步函数的那一行之后的下一行。 同时,新线程将执行一些代码(异步函数的主体),并最终执行作为参数传递的块,最后该线程被杀死并且不再存在。 注:除了阅读该函数的文档之外,没有其他方法可以知道该函数是同步的还是异步的 )。

现在,让我们回到您的代码。

[client loginToMistarWithPin:@"20014204" password:@"yuiop" success:^{
    [client getUserID:^(NSString *result) {
        NSString *userIDWithHTML = [self userIDRegex:result];
        NSString *userID = [self onlyNumbersRegex:userIDWithHTML];

        // PLACE HERE THE CODE TO EXECUTE WHEN SUCCESSFULLY LOGGED IN
        [anotherClassInstance someMethod:userID];

    }];
} failure:^{
    NSLog(@"login failed from controller");
}];

用户登录后应执行的所有操作均应放置在块内(如果功能是同步的,则可以将其放置在块后)。 要将userID发送到另一个类,只需像在代码的其他任何部分中一样调用该类的方法。

我认为没有必要使用委托(尽管只有您会知道,因为您是应用程序的架构师)。

正如@santhu所说,请使用委托模式或通知模式。 同时使用它们都是一种常见的做法。 通常,委托是正确的方法,但有时您需要通知。 两者都适用于您的所有基础。

在确定它们的工作方式以及有关其工作方式的完整详细信息之前,请先对其进行查找,但基本上是:

[client getUserID:^(NSString *result) {
    NSString *userIDWithHTML = [self userIDRegex:result];
    NSString *userID = [self onlyNumbersRegex:userIDWithHTML];

    // delegate pattern:
    if ([self userIdIsValid:userID]) {
      if (self.delegate && [self.delegate respondsToSelector:@selector(foundValidUserID:)]) {
        [self.delegate foundValidUserID:userID];
      }
    } else {
      if (self.delegate && [self.delegate respondsToSelector:@selector(foundInvalidUserID:)]) {
        [self.delegate foundInvalidUserID:userID];
      }
    }

    // notification pattern:
    if ([self userIdIsValid:userID]) {
      [[NSNotificationCenter defaultCenter] postNotificationName:MyFoundValidUserIDNotification object:self userInfo:@{@"userID": userID}];
      }
    } else {
      [[NSNotificationCenter defaultCenter] postNotificationName:MyFoundInvalidUserIDNotification object:self userInfo:@{@"userID": userID}];
    }


}];

第三种选择是,您可以使用块回调...这是块上的新孩子执行的操作...这里没有明确定义的模式,块是全新的,并且委托/通知已使用20年。 但是这是我使用块定义回调的方法:

typedef void (^UserIdCallbackBlock)(NSString *userID);

- (void)parseForUserIDOnSuccess:(UserIdCallbackBlock)successCallback onFailure:(UserIdCallbackBlock)failureCallback {
  ...

  NSString *userID = [self onlyNumbersRegex:userIDWithHTML];

  if ([self userIdIsValid:userID]) {
    successCallback(userID);
  } else {
    failureCallback(userID);
  }

  ...
}

关于您的评论,我想给您一个提示:

出于代码可读性的考虑,这并不是说我还有一个任务要做,我放在此代码块中的内容还将包含一个代码块和另一个代码块。

这是典型的异步模式-称为“连续”。

给定的是,您还应该实施适当的错误处理 ,还应该提供一种在任何时候取消异步任务的整个“链”的方法,使用NSOperationQueuesNSOperations ,dispatch_queue和块,NSNotifications或委托的典型解决方案将不可避免地成为过于复杂,复杂且难以被他人理解。 (这里已经有一个答案可以证明这个宏伟;))

因此,只要问题变得更加复杂并且“内置框架”无法提供舒适的解决方案,第三方库就会为您提供帮助。

但是首先,根据您的评论,让我们举一个简单的例子:

这并不是说我还有一个任务要做,我放在这个区块中的东西也会有一个区块,另一个区块和另一个

好的,假设您的目标实际上是:

  1. 异步执行Web服务的登录。
  2. 然后,如果成功,则以JSON异步方式获取对象列表。
  3. 然后,如果成功,则解析JSON响应。
  4. 然后,如果成功,则将对象插入托管对象上下文中,并异步保存托管对象上下文链并使其持久化。
  5. 当以上所有操作成功后,请更新主线程上的UI
  6. 如果出现任何故障,报告失败任务的错误

我将展示一个使用实现“ promises”的库的解决方案(请参阅Wiki Future and promises )看起来如何:

事不宜迟,也没有详尽的解释,“ Promise”是什么意思,假设我们在View Controller中定义了一个方法,该方法声明为:

- (RXPromise*) loginToMistarWithPin:(NSString*)pin 
                           password:(NSString*)password;  

注意 :上面的方法是异步的 ,其功能等效于以下形式:

        typedef void (^completion_t)(id result, NSError*error);
        - (void) loginToMistarWithPin:(NSString*)pin 
                             password:(NSString*)password 
                           completion:(completion_t)completion;

然后假设您的View Controller中有另一个方法,可以从远程服务器(也可以是异步的)中获取对象:

- (RXPromise*) fetchObjects;

然后,假设我们有一个类CoreDataStack ,该类由“根上下文”组成,该“根上下文”保存到具有子托管对象上下文(即“主上下文”)的持久性存储中,该子托管对象上下文与主线程关联。

CoreDataStack定义了此方法,该方法保存了一系列托管对象上下文,该对象基本上是已设置的:childContext-> main_context-> root_context:

- (RXPromise*) saveWithChildContext:(NSManagedObjectContext*)childContext;

然后,步骤1至5中所述的整个任务可以表示如下:

[client loginToMistarWithPin:@"20014204" password:@"yuiop"]
.then(^id(id result){
    // login succeed, ignore result which is @"OK"

    // Now fetch the objects with an asynchronous network request, 
    // returning JSON data as a NSData object when it succeeds:
    return [client fetchAllUsers];
}, nil)
.then(^id(NSData* json){
    // The network request succeeded, and we obtain the JSON as NSData.
    // Parse it and get a Foundation representation:
    NSError* error;
    id jsonArray = [NSJSONSerialization JSONObjectWithData:json 
                                                   options:0 
                                                     error:&error];
    if (jsonArray) {
        return jsonArray;  // handler succeeded
    }
    else {
        return error;      // handler failed
    }        
}) 
.then(^id(NSArray* objects){
    // Parsing succeeded. Parameter objects is an array containing 
    // NSDictionaries representing a type "object".

    // Save into Core Data:
    // Create a managed object context, which is a child of the 
    // "main context" of a Core Data stack:
    NSManagedObjectContext* moc = [[NSManagedObjectContext alloc]
                      initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    moc.parentContext = self.coreDataStack.managedObjectContext;
    // Create managed objects and initialize them with the given 
    // NSDictionary:
    for (NSDictionary* object in objects) {
        // note: `createWithParameters:inManagedObjectContext` executes on
        // the context's queue
        [Object createWithParameters:object inManagedObjectContext:moc];
    }
    // Finally, asynchronously save into the persistent store and
    // return the result (a RXPromise):
    return [self.coreDataStack saveWithChildContext:moc]; 
}, nil)
.thenOn(dispatch_get_main_queue(), ^id(id result){
    // Saving to the backing store succeeded. Now, we possibly want to 
    // update some UI on the main thread. We are executing on the main
    // thread already (see thenOn(dispatch_get_main_queue())
    ...
    [self.tableView reloadData];
    return nil;
}, nil)
.then(nil, ^id(NSError* error){
    // If something went wrong in any of the above four steps, the error 
    // will be propagated down and "cought" in this error handler:
    NSLog(@"Error: %@", error);
});

免责声明:我是GitHub上提供的RXPromise库的作者。 还有一些实现Promises的Objective-C库。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM