如何从块中返回值?

时间:2018-01-23 01:39:33

标签: objective-c macos objective-c-blocks xpc

我很遗憾地承认我对块没有任何线索。

我正在编写一个GUI卸载程序应用程序,它使用特权助手工具删除我所有产品的文件和目录。

因为帮助器是单个文件,所以我通过创建一个TEXT段然后将Info.plist复制到其中来将Info.plist嵌入到可执行文件中。

到目前为止,我可以成功使用SMJobBless()/Library/PrivilegedHelperTools及其/Library/LaunchDaemons中的launchd属性列表中安装该帮助工具。

Apple建议使用XPC来确保GUI应用程序和工具具有二进制兼容版本。 EvenBetterAuthorizationSample使用块来调用辅助工具。我需要使用getVersion函数将NSMutableString传回verifyVersionCompatibility

- (uint32_t)getVersion: (NSMutableString**) helperVersionPtr
    // Called when the user clicks the Get Version button.
    // This is the simplest form of
    // NSXPCConnection request because it doesn't require any authorization.
{
    assert( helperVersionPtr != NULL );
    assert( *helperVersionPtr != NULL );

    [self connectAndExecuteCommandBlock:^(NSError * connectError) {
        if (connectError != nil) {
            [self logError:connectError];
            *helperVersionPtr = NULL;

            DBG_MSG(( "%s connectAndExecuteCommandBlock failed connectError: %s\n", __func__, [[connectError description] UTF8String] ));

        } else {
            [[self.helperToolConnection remoteObjectProxyWithErrorHandler:^(NSError * proxyError) {
                [self logError:proxyError];
            }] getVersionWithReply:^(NSString *version) {

                [self logWithFormat:@"version = %@\n", version];

                [*helperVersionPtr setString: version];  // Pass the version back
            }];
        }
    }];

    return 0;
}

块在这里执行:

- (void)connectAndExecuteCommandBlock:(void(^)(NSError *))commandBlock
    // Connects to the helper tool and then executes 
    // the supplied command block on the 
    // main thread, passing it an error indicating 
    // if the connection was successful.
{
    assert([NSThread isMainThread]);

    // Ensure that there's a helper tool connection in place.

    [self connectToHelperTool];

    // Run the command block.  
    // Note that we never error in this case because, if there is 
    // an error connecting to the helper tool, it will be delivered 
    // to the error handler 
    // passed to -remoteObjectProxyWithErrorHandler:.
    // However, I maintain the possibility 
    // of an error here to allow for future expansion.

    commandBlock(nil);
}

帮助工具的getVersionWithReply:

- (void)getVersionWithReply:(void(^)(NSString * version))reply
    // Part of the HelperToolProtocol.
    // Returns the version number of the tool.  Note that never
    // requires authorization.
{
    // We specifically don't check for authorization here.
    // Everyone is always allowed to get
    // the version of the helper tool.

    reply([[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]);

}

1 个答案:

答案 0 :(得分:1)

认为你问的是-getVersion: 如何同步获取通过XPC回复功能获得的字符串值并将其返回给调用者。

问题是所有XPC消息/处理程序都是在异步和随机线程上执行的。

如果你真的必须进行同步通话,你可以使用信号量来阻止,直到收到回复:

- (NSString*)synchronousExample
{
    NSConditionLock* barrierLock = [[NSConditionLock alloc] initWithCondition:NO];
    id<YourXPCServiceMessaging> proxy = self.serviceProxy;  // get the proxy object of the XPC connection

    // Send the XPC message that requests a response
    __block NSString* replyString = nil;
    [proxy someMessageWithStringReply:^(NSString* string){
        // The XPC service has responded with the value; squirrel it away and let the original thread know it's done
        replyString = string;
        [barrierLock lock];
        [barrierLock unlockWithCondition:YES];
        }];
    // Wait for the reply block to execute
    [barrierLock lockWhenCondition:YES];
    [barrierLock unlock];

    return replyString;
}

如果您可以重新组织代码,一种更简单的方法是发出异步请求,然后在收到回复后继续:

- (void)startVerifyCheck
{
    id<YourXPCServiceMessaging> proxy = self.serviceProxy;  // get the proxy object of the XPC connection
    [proxy someMessageWithStringReply:^(NSString* string){
        // The XPC service has responded with the value
        if ([string isEqualToString:@"the expected value"])
            {
            // Verify successful: continue with the next step
            dispatch_async(dispatch_get_main_queue(), ^{
                [self verifyComplete];
                });
            }
        }];
    // Reply block will continue once the reply is received
}

- (void)verifyComplete
{
    // do the next step here...
}