如何从异步属性的getter返回?

时间:2013-04-04 07:31:41

标签: ios objective-c grand-central-dispatch getter-setter

我已经过度使用了一个请求在线服务获取结果的getter。 如何强制getter仅从同步块返回结果?

@interface MyClass ()

@property (nonatomic, strong) NSMutableDictionary* myDictionary;
@end 

@implementation MyClass

-(NSMutableDictionary*) myDictionary {

    dispatch_async(queue, ^{
         /* perform online request */
         dispatch_sync(dispatch_get_main_queue(), ^{
             // I need to obtain lock until this line gets executed and only then return
         });
    });
}
@end

使用相当不错的Google搜索至少3小时,我遇到了 dispatch_group_async dispatch_semaphore __ block 。我不知道我是否错误地使用它们但是没有达到目的。

更新1:

myDictionary是一个异步属性。我想看看这是否可以通过getter本身来实现。

3 个答案:

答案 0 :(得分:4)

@Kishor当请求真正异步时,UI不会阻塞。您的“异步”请求阻止UI的原因是,实际上它们不是同步的。这就是伪代码中的原因:

- (double) notTruelyAsyncValue 
{
    __block double someExpensiveDoubleToCompute = 0.0;

    // One of the many ways that we can coordinate concurrent threads.
    dispatch_semaphore_t sema_done = dispatch_semaphore_create(0);

    dispatch_async(not_the_main_queue, ^(void) {
       // Simulate long processing time.
       sleep(5); 
       someExpensiveDoubleToCompute = 3.1415926535;
       dispatch_semaphore_signal(sema_done);
    });

    // We can't return until the async block has returned.
    // So we wait until it's done. If we wait on the main queue
    // then our UI will be "frozen". 
    dispatch_semaphore_wait(sema_done, DISPATCH_TIME_FOREVER);

    // Now we have our result we free the resources and return
    dispatch_release(sema_done);

    return someExpensiveDoubleToCompute;
}

如果从异步线程调用此方法,它将不会阻止UI,但如果从主队列/线程调用它,则它将阻止UI,因为您正在等待主线程上的信号量。无论你如何实现等待,它总是如此 阻止UI,因为主线程是一个串行队列。这意味着在异步块完成之前,主队列上的其他块或事件都不会运行。

如果您不想阻止UI,请不要调用可能阻塞主线程的任何内容。一个好的模式是使用@Collin建议的完成块。模式如下:

- (void) computeAnyncValueWithCompletionBlock:((void)^(double value))completionBlock 
{
     dispatch_async(not_the_main_queue, ^(void) {
          // do some expensive computation.
          double value = 3.1415926535;
          completionBlock(value);
     });
}

这可以从任何地方调用,永远不会阻止。

答案 1 :(得分:3)

如果我不清楚,请纠正我 - 你想要做的是在后台下载,同时还以非同步方式从方法返回?如果您考虑到这一点,那么您可能会尝试同时执行两个相互矛盾的事情:该方法必须阻塞,直到它返回异步返回。

我认为你想要的是一个完成块。您可以改为创建第二个方法,而不是覆盖myDictionary,而不是覆盖- (void)downloadWithCompletion:(void(^)(NSDictionary *dictionary))completion { dispatch_async(queue, ^{ /* perform online request */ // Create an NSDictionary from what was downloaded. NSDictionary *dictionary = <parsed request data> dispatch_sync(dispatch_get_main_queue(), ^{ // Call the completion block using the info that was downloaded // where self.myDictionary could be set. completion(dictionary); }); }); }

{{1}}

答案 2 :(得分:3)

事实证明,并不困难。我试图用我的发现更新这个,我会继续更新。

异步属性的getter应该如何表现?

  1. Perform asynchronous request如果属性不可用且set the property。 (对于延迟加载)
  2. 在物业可用时归还。
  3. <强>挑战:

    • 用户界面冻结
    • 无效退货

    虽然,它可能会让人感到困惑,因为为什么不在这里使用同步方法?,答案是它会冻结用户界面。

    没有人知道何时完成异步请求,但这并不意味着对可用性状态的全部需求应该是未知的。从硬件,内核到更高级别的API,所有系统都有机制。您可以参考协议和代理作为沟通方式之一。


    为什么不使用协议和代理来实现异步属性?

    1. 我将不得不在所有引用类中强制执行委托 - &gt; 不是吸气剂
    2. 我不希望其他类知道它是一个异步属性,如果他们想要数据,他们会在可用的时候得到它而不知道它的检索方式。 (obv没有冻结UI)。

    3. 我们如何在不使用协议和代理或将其转换为同步调用的情况下实现此目的?

      答案使用条件变量。请注意,这个条件变量与我们用于分支的变量不同。它必须是线程安全的,并且在编译器和内核级别受支持。

      1. NSCondition
      2. 来自官方文档,

        The NSCondition class implements a condition variable whose semantics follow 
        those used for POSIX-style conditions. A condition object acts as both a lock 
        and a checkpoint in a given thread. The lock protects your code while it tests 
        the condition and performs the task triggered by the condition. The checkpoint 
        behavior requires that the condition be true before the thread proceeds with its 
        task. While the condition is not true, the thread blocks. It remains blocked until 
        another thread signals the condition object.
        

        我所要做的就是让这个getter方法在不使用委托的情况下了解异步请求的完成情况。

        -(NSMutableDictionary*) myDictionary {
            if(!_myDictionary) {
                _myDicitonary = [self someOtherMethod];
            }
            return _myDictionary;
        }
        

        虽然可以在getter本身中实现锁和异步请求,但我拒绝这样做以便于操作锁。此外,这是一个很好的逻辑分离:)

        - (NSMutableDictionary *)someOtherMethod
        {
            NSCondition *lockForCompletion = [[NSCondition alloc] init];
            __block BOOL available = NO;
            __block NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] init];
        
            [lockForCompletion lock]; // acquire the lock
        
                dispatch_async(queue, ^{
                     /* perform online request */
                     dispatch_sync(dispatch_get_main_queue(), ^{
                         [tempDict setObject:myResponse forKey:@"mykey" count:1];
                         available = YES;
                         [lockForCompletion signal];
                     });
                });
            while(!available) {
                [lockForCompletion wait];
            }
        
            [lockForCompletion unlock];
            return tempDict;
        }
        

        我还想指出,最初布尔谓词available似乎根本没有必要,因为wait将禁止控制超出它。但实际上,布尔谓词在保持锁定方面起着非常重要的作用,如文档中所述。

        A boolean predicate is an important part of the semantics of using conditions 
        because of the way signaling works. Signaling a condition does not guarantee 
        that the condition itself is true. There are timing issues involved in signaling 
        that may cause false signals to appear. Using a predicate ensures that these 
        spurious signals do not cause you to perform work before it is safe to do so. 
        The predicate itself is simply a flag or other variable in your code that you test 
        in order to acquire a Boolean result.