【问题标题】:Make iOS blocks execute synchronously使 iOS 块同步执行
【发布时间】:2011-07-07 06:38:19
【问题描述】:

如何让block同步执行,或者让函数在return语句之前等待handler,这样数据才能从block传回来?

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    return data;
}

【问题讨论】:

  • 已编辑以制作更具体的代码 sn-p
  • 在任何时候,如果您遇到需要进行异步同步的情况,您的设计就会出现严重错误。
  • 只是好奇,你为什么要这样做?如果您只想在其他地方完成块,那么您的“performRequest”方法还应该包括一个应该在内部块完成中调用的块。正如 Kunal 指出的那样,您的设计有问题。

标签: iphone ios cocoa-touch ios4 objective-c-blocks


【解决方案1】:

在这种情况下,您可以使用信号量。

-(id)performRequest:(id)args
{
    __block NSData *data = nil;   
     dispatch_semaphore_t sem = dispatch_semaphore_create(0);
     [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
       data = [NSData dataWithData:responseData];
       dispatch_semaphore_signal(sem);
     }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    return data;
}

信号量将阻止进一步语句的执行,直到收到信号,这将确保您的函数不会过早返回。

【讨论】:

  • 这是正确的答案,假设你没有在主线程上执行。
  • 这种方法只能用于异步方法,这些方法在调用线程之外的单独线程上运行和调用完成。
  • 注意:如果你在主线程上你仍然可以使用信号量,只是需要稍微修改一下,见这里:stackoverflow.com/a/4326754/2057171
【解决方案2】:

异步几乎总是更好。但如果你想要同步:

-(id)performRequest:(id)args
{
__block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
    }];

    while(!data) {
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    }
    return data;
}

免责声明: CocoaCouchDeveloper 说当然这只有在完成块和运行循环在同一个线程上时才有效。 我假设因为我知道很多(大多数)COMPLETION 处理程序都是这样工作的,但原则上它是有效的。

以上不是线程安全的
使用信号量或其他东西。
我也说过我不宣传这个

【讨论】:

  • 免责声明:内嵌编写。 [可能不是 100%,但清楚地表明你必须做什么]
  • 你能保证对变量 data 的访问是线程安全的并且涉及内存屏障吗?我怀疑它是。如果没有内存屏障,编译器就有资格将 data 保存在寄存器中,因此 data 永远不会在调用线程中更新。
  • 嗯,实际上,只有在完成处理程序在执行运行循环的同一线程上执行时才可靠。我刚刚做了一个简化的测试,结果证明:数据变量将保存在一个永远不会更新的寄存器中(并且 while 循环永远不会返回)。也没有内存屏障,这意味着,如果要更新数据,则不能保证对它的访问是原子/安全的,并且返回的指针值可能会被削弱。但是:Objective-C 将阻止编译器使用许多优化机会,并且增加它仍然可以工作的机会;)
  • OP的问题很笼统。我们永远不应该假设完成处理程序是在主线程上执行的,除非它被明确记录。事实上,在“众所周知的”线程上执行处理程序是不好的做法,因为它会增加死锁的机会。在一些例外情况下,在主线程上执行处理程序是有意义的:例如 UIViews 动画。 AFN 不属于这个组,很难——即使它使用主线程。相反,在 AVFoundation 中,处理程序将使用私有线程(请参阅“使用 AV Foundation 进行并发编程”)。
【解决方案3】:

您可以添加一个方法来处理返回的数据并在您的块中调用它:

-(void)performRequest:(id)args{
    __block NSData *data = nil;   

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
        data = [NSData dataWithData:responseData];
        [self processData:data]; //method does something with the data 
    }];
}

【讨论】:

    【解决方案4】:

    你可以像下面的代码在另一个线程中做同步请求

    -(void)performRequest:(id)args
    {
    
     NSURLResponse *response = nil;
     NSError *error = nil;
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    }
    

    你可以从主线程调用这个

    [self performSelectorInBackground:@selector(performRequest:) withObject:args];
    

    否则您可以使用以下方法进行异步请求

    [NSURLConnection alloc]initWithRequest:request delegate:self];
    

    并为 NSURLConnection 实现委托方法

    【讨论】:

    • 如果我需要访问一个除了使用块之外别无选择的类,例如:[classxyz requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOLgranted, NSError *error){}];没有其他选择,如果我错了,请纠正我
    • 您可以按照我在回答中所说的那样实现委托方法,控制将来自 connectionDidFinishLoading: 如果您是从服务器请求,则请求成功完成
    【解决方案5】:

    你确定要同步吗? 如果是,您可以在您的块中调用(或放置)您的处理程序函数或使用 Jignesh 建议(并在您的处理程序完成并且想要返回值时使用“performSelectorInMainThread”。

    异步方式(有点)更难,但更好的是: - 它迫使你编写干净的代码(不传递方便的变量) - 您可以执行其他操作,这样用户就不会等待并认为您的应用程序很慢。

    你真的应该给它两三个小时去异步。小痛换全利。 你也可以看看 Key-Value Observing。

    【讨论】:

      【解决方案6】:

      你可以这样做。

      -(id)performRequest:(id)args
      {
      __block NSData *data = nil;   
      
       [xyzclass performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse          *urlResponse, NSError *error) {
      
           dispatch_sync( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{
      
           data = [NSData dataWithData:responseData];
      
           });
      
       }];
      
      return data;
      }
      

      【讨论】:

      • 我希望看到一个很好的简单教程来理解这一点,因为到目前为止,教程太基础且无用,例如实用工作室的乘数示例,或者太晦涩而无用。你知道吗?
      • 在处理程序被调用之前这不会到达return 语句吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      相关资源
      最近更新 更多