【问题标题】:How to handle an asynchronous callback in a synchronous function call?如何处理同步函数调用中的异步回调?
【发布时间】:2012-12-25 17:41:51
【问题描述】:

我有一个在回调中异步返回对象(例如 UserProfile)的方法。

基于此 UserProfile 对象,一些代码计算 UITableViewCell 是否可编辑:

我创建了以下代码,但很遗憾它不起作用。

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{        
    Entry *entry = [[self.feed entries] objectAtIndex:[indexPath row]];
    typedef BOOL (^BOOLBlock)(id);

    BOOLBlock bar = ^BOOL (id p) {
        if (p) {
            UserProfile *user = (UserProfile *) p;
            NSEnumerator *e = [[entry authors] objectEnumerator];
            id object;
            while (object = [e nextObject]) {
                if ([[object name] isEqualToString:[[[user authors] objectAtIndex:0] name]])
                    return TRUE;
            }
            return FALSE;
        } 
        else {
            return FALSE;
        }
    };

    [[APPContentManager classInstance] userProfile:bar];    
}

在最后一行,它说不兼容的块指针类型发送

'__strong BOOLBlock' (aka 'BOOL (^__strong)(__strong id)') to parameter of type 'void (^)(UserProfile *__strong)'

APPContentManager.h

-(void)userProfile:(void (^)(UserProfile *user))callback;

【问题讨论】:

  • 您的 AppContentManager userProfile 方法似乎不期望具有布尔返回值的块。
  • 此外,您应该知道委托方法 canEditRowAtIndexPath 是立即返回布尔值而不是异步的事实。您应该返回数据建模板,看看是否可以在需要时立即将所需信息保留在手边(例如,通过使用该值预填充 userProfile 模型)。
  • 您也没有在该方法中返回任何值。
  • 直到,感谢您的快速回复。 1.)我添加了相关方法的签名。你是对的,在它当前的实现中,它不期望一个布尔返回值。有没有办法更改 canEditRowAtIndexPath 中的代码而不是 userProfile 中的代码? 2.)感谢您的提示。实际上,UserProfile 缓存在 APPContentManager 中,所以会立即返回。
  • 请参阅@NoahWitherspoon 解决方案 - 尽管我个人会朝不同的方向前进,只需向模型类 (userProfile) 添加一个方法,让我知道是否可以编辑该配置文件。这样你就不需要使用信号量了——信号量是一种相当重的武器......

标签: iphone objective-c ios callback objective-c-blocks


【解决方案1】:

-userProfile: 方法不期望您的 BOOLBlock 类型——它不负责返回任何内容。你想在这里使用信号量,但你应该记住 Till 的 cmets 关于 -tableView:canEditRowAtIndexPath: 的预期同步性——如果你的 userProfile: 方法需要一段时间,你肯定应该预先缓存这个可编辑性信息。

- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {        
    Entry *entry = [[self.feed entries] objectAtIndex:[indexPath row]];
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    __block BOOL foundAuthor = NO;

    [[APPContentManager classInstance] userProfile:^(UserProfile *user) {
        NSEnumerator *e = [[entry authors] objectEnumerator];
        id object;
        while (object = [e nextObject]) {
            if ([[object name] isEqualToString:[[[user authors] objectAtIndex:0] name]]) {
                foundAuthor = YES;
                break;
            }
        }
        dispatch_semaphore_signal(sema);
    }];
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    dispatch_release(sema);

    return foundAuthor;
}

【讨论】:

  • 会的。我假设-userProfile: 方法使用块是为了方便而不是因为它需要一段时间才能运行。
  • 知道了,我假设作者实际上在那个 userProfile 方法调用的后台使用了异步数据检索。因此,为了清楚地说明,如果 userProfile 调用实际上需要时间来完成(例如,通过一些网络获取),请不要使用此建议。否则,看门狗会咬你;)
  • 是的,在 canEditRowAtIndexPath 中调用 userProfile 时,为了方便起见,它使用了一个块。它只会在应用程序生命周期的某个早期状态下异步调用一次,即用户登录时,更准确地说。
  • 以前从未在 iOS 上使用过信号量,在 "dispatch_release(sema);" 行出现 (lldb) 异常使用上面建议的代码时。
  • 不应该是dispatch_semaphore_create(0)吗?如果使用 1 创建它,则必须调用 wait 两次。
猜你喜欢
  • 2017-06-13
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 2012-07-25
  • 1970-01-01
  • 2013-07-12
  • 2020-12-20
  • 2015-03-19
相关资源
最近更新 更多