Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I return from a getter of an asynchronous property?

I have over-ridden a getter that requests an online service to get a result. How do I force the getter to return the result only from synchronous block ?

@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

With a pretty good googling for at least 3 hrs I came across dispatch_group_async, dispatch_semaphore and __block. I don't know if I was using them wrong but didn't serve the purpose.

Update 1 :

myDictionary is an asynchronous property. I want to see if this can be implemented via the getter itself.

like image 506
Kishor Kundan Avatar asked Apr 04 '13 07:04

Kishor Kundan


2 Answers

@Kishor When a request is truely asynchronous the UI does not block. The reason your "asynchronous" request block the UI is that, in fact they are not asychronous. Here is why in pseudo code:

- (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;
}

If you call this method from an asynchronous thread it will not block the UI but if you call it from the main queue/thread then it WILL block the UI because you are waiting on a semaphore on the main thread. Regardless of how you implement your waiting, it will always block the UI because the main thread is a serial queue. That means no other block or event on the main queue will run until your asynchronous block is completed.

If you don't want to block your UI then don't call anything that might block from the main thread. A good pattern for this is to use completion blocks as suggested by @Collin. The pattern is as follows:

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

This can be called from anywhere and will never block.

like image 129
aLevelOfIndirection Avatar answered Nov 08 '22 16:11

aLevelOfIndirection


Correct me if I'm unclear -- what it sounds like you want to do is download in the background, while also returning from the method in a non-async fashion? If you think about that, you're kind of trying to do two contradictory things at once: the method either has to block until it returns or return asynchronously.

What I think you want is a completion block. Instead of overriding myDictionary, you might instead create a second method that does something like this:

- (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);
         });
    });
}
like image 42
Collin Avatar answered Nov 08 '22 15:11

Collin