我如何 return 一个块中的值?

How can I return a value from a block?

我很谦虚地承认我对块一无所知。

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

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

到目前为止,我可以成功地使用 SMJobBless()/Library/PrivilegedHelperTools 中安装该辅助工具,并在 /Library/LaunchDaemons 中安装它的 属性 列表。

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"]);

}

认为你问的是如何-getVersion:同步获取通过XPC回复获取的字符串值函数并return它给调用者。

问题是所有 XPC messages/handlers 在随机线程上异步执行。

如果你真的必须有一个同步调用,你可以使用信号量阻塞直到收到回复:

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