Droopycom
Droopycom

Reputation: 1931

How to synchronously wait for reply block when using NSXPCConnection

I'm using NSXPCConnection and one of my interface call has a reply block, like this:

- (void)addItem:(NSData *) withLabel:(NSString *) reply:(void (^)(NSInteger rc))reply;

Which I call like this:

__block NSInteger status;
[proxy addItem:data withLabel:@"label" reply:^(NSInteger rc)
         {
             status = rc;
         }
];

My understanding is that the reply block run asynchronously, and potentially after the method returns.

I want to test the return code synchronously, what's the best way to do it?


To clarify further the snippet above: the proxy object is the remote object obtained from an NSXPCConnection object using the remoteObjectProxy method. This is an important detail as this impact on which queue the reply block is invoked.

Upvotes: 3

Views: 4410

Answers (4)

Droopycom
Droopycom

Reputation: 1931

I just found out a probably better way to do this:

Use synchronousRemoteObjectProxyWithErrorHandler instead of remoteObjectProxy when creating the remote object.

No need for semaphore or group.

Upvotes: 13

I propose to use dispatch_semaphore.

// Create it before the block:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSInteger status = 0;
[proxy addItem:data withLabel:@"label" reply:^(NSInteger rc) {
             status = rc;
             // In the block you signal the semaphore:
             dispatch_semaphore_signal(semaphore);
         }
];

// here wait for signal
// I dont remember exactly function prototype, but you should specify here semaphore and the time waiting (INFINITE)
dispatch_semaphore_wait(...);

// in non-ARC environment dont forget to release semaphore
dispatch_release(semaphore);

return status;

Upvotes: 2

Leland Wallace
Leland Wallace

Reputation: 228

This is what dispatch groups were made for.

NSTimeInterval timeout = 120; // in seconds
__block NSInteger status;

dispatch_group_t syncGroup = dispatch_group_create();
dispatch_group_enter(syncGroup);

[proxy addItem:data withLabel:@"label" reply:^(NSInteger rc)
    {
        status = rc;
        dispatch_group_leave(syncGroup);
    }
];

dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * timeout));

if(dispatch_group_wait(syncGroup, waitTime) != 0)
{
    // complain about a request timing out
}

// enjoy your status

if you chose to use remoteObjectProxyWithErrorHandler to get your proxy, then you need to remember to also put a call to dispatch_group_leave(syncGroup) in your error handler.

Upvotes: 5

bbum
bbum

Reputation: 162722

I want to test the return code synchronously, what's the best way to do it?

You really don't want it to run synchronously. That'll just block the queue/thread that the block is running on and generally cause havoc to crashes.

Instead, after the status = rc; line, make a call to something that can process the fact that it is done. Let the method return, let the queue or event loop run, then do the work needed whenever addItem:withLabel: is done.


Like this:

__block NSInteger status;
[proxy addItem:data withLabel:@"label" reply:^(NSInteger rc) {
             status = rc;
             // like this ...
             [someObject processReturnedStatus];
         }
];

Upvotes: 2

Related Questions