objective c block that waits for another delegate

2019-06-12 20:44发布

I have a watchkit app that calls a viewcontroller on an iphone app. I have a delegate for a network connection. I'm trying to use a block so that I don't tightly couple my AppDelegate and my view controller too closely. How can I notify my block when the delegate is finished?

ViewController.m

-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
   [self setUpAppForWatch];
   completion(YES);
}

-(void)finishedMessageParse:(NSMutableData *)messageData{
   //the delegate is finish tell the block completion is done.

}

-(void)setUpAppForWatch{
   [network call];
}

AppDelegate.m

-(void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)
(NSDictionary *))reply{

[vc getWatchDataWithCompletion:^(BOOL gotData){
    if (gotData){
       //I'm done reply dictionary
       reply(@{@"data":serlizedData})
}];

2条回答
手持菜刀,她持情操
2楼-- · 2019-06-12 21:11

add new property in viewcontroller:

@property (nonatomic, strong) void(^completion)(BOOL gotData);


-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
   [self setUpAppForWatch];
   self.completion = completion;
}

-(void)finishedMessageParse:(NSMutableData *)messageData{
    if (self.completion){
        self.completion(YES);
    }
}
查看更多
迷人小祖宗
3楼-- · 2019-06-12 21:23

There're three possible ways.

;tldr - refer to the third one. Else - read everything, it might be useful.

First one

Use private serial queue for performing tasks of finished... method and your block. It will suffice you in case, if finished... always called before block. If not - take a look at the Second one

Use private @property dispatch_queue_t privateSerialQueue; of View Controller.

privateSerialQueue = dispatch_queue_create("PrivateQueue", DISPATCH_QUEUE_SERIAL);

Than, use it like this

-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
    [self setUpAppForWatch];
    dispatch_async(privateSerialQueue, ^(){
       completion(YES);
    });
}

-(void)finishedMessageParse:(NSMutableData *)messageData{
    dispatch_sync(privateSerialQueue, ^(void){
            //Here goes whatever you need to do in this method before block start
    });
    //the delegate is finish tell the block completion is done.
}

Second one

Take a look at dispatch_semaphore_t. Make it a public property of your View Controler

@property (readonly) dispatch_semaphore_t semaphore

Create it with starting value 0. It will let you wait in case your block runs before delegate finished... method and run immediately, if finished has already completed before block. Like this

self.semaphore = dispatch_semaphore_create(0); 

Then you can use it this way

-(void)finishedMessageParse:(NSMutableData *)messageData{
  //the delegate is finish tell the block completion is done.
  dispatch_semaphore_signal(self.semaphore);
}

[vc getWatchDataWithCompletion:^(BOOL gotData){
    if (gotData){
       //I'm done reply dictionary
       dispatch_semaphore_wait(vc.semaphore, DISPATCH_TIME_FOREVER);
       reply(@{@"data":serlizedData})
}];

Third one

Came to my mind while writing the two above =) Some kind of combination of previous two

Use private property of your view controller @property (readonly) dispatch_semaphore_t semaphore

Initialize it the same way, as in the second (with starting value 0)

self.semaphore = dispatch_semaphore_create(0); 

And use it privately like this

-(void)getWatchDataWithCompletion:(void(^)(BOOL gotData))completion{
    [self setUpAppForWatch];
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    completion(YES);
}

-(void)finishedMessageParse:(NSMutableData *)messageData{
  //the delegate is finish tell the block completion is done.
  dispatch_semaphore_signal(self.semaphore);
}

P. S. Hope, it helps you to get to the point. Feel free to ask anything not clear

查看更多
登录 后发表回答