在Objective-c中,数组中的Swift闭包变为nil

时间:2017-06-21 19:20:49

标签: ios objective-c memory-management swift3 objective-c-blocks

我创建了一个Objective-c方法,它将通过NSInvocation调用一个方法:

typedef void (^ScriptingEmptyBlock)();
typedef void (^ScriptingErrorBlock)(NSError *error);
- (void)scripting_execute:(NSString *)operation withParams:(nullable NSArray *)args {

    SEL selector = [self scripting_selectorForOperation:operation];
    Class class = [self class];
    NSMethodSignature *signature = [class instanceMethodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

    [invocation setSelector:selector];
    [invocation setTarget:self];
    for (int idx = 0; idx < args.count; idx ++) {
        id arg = args[idx];
        [invocation setArgument:&arg atIndex:idx + 2];

    }

    ScriptingEmptyBlock success = args[1];

    // Breakpoint added on next line to test for nil
    success(); // this is nil and would crash!
    // (lldb) po args.count
    // 3

    // (lldb) po success
    // Printing description of success:
    // (ScriptingEmptyBlock) success = 0x0000000000000000

    // (lldb) po args[1]
    // (Function)

    //[invocation getArgument:&success atIndex:2]; // also tried this and got nil as well
    [invocation invoke];

}

该方法采用“操作”,通过覆盖子类中的scripting_selectorForOperation:将其转换为选择器,然后执行调用。

所有这些都有效,除非被调用的方法具有块参数,它们是nil,我添加了我用注释描述的nil的测试,当尝试从数组中读取闭包时它将是nil。

叫做:

let successClosure: ScriptingEmptyBlock = {
                    print("Renamed product")
                }
let errorClosure: ScriptingErrorBlock = { error in
                    print("Failed to rename product: \(error)")
                }
let params:[Any] = [ "testName", successClosure, errorClosure]
object.scripting_execute (ScriptOperation.updateProductName.rawValue, withParams: params)

为什么关闭成为零?

1 个答案:

答案 0 :(得分:3)

success nil(事实上,NSArray不能包含nil s)。如果您将其打印为NSLog(@"%@", success);,则会显示(Function),而不是(null)。如果您打印类似NSLog(@"%@", [success class]);的类,则会显示_SwiftValue。基本上,它是一个Swift值,它被桥接到Objective-C。

问题是对象success指向的不是Objective-C块。它是一个Swift闭包,Swift闭包与Objective-C块不同。尝试使用调用它就好像它是一个Objective-C块导致未定义的行为。调试器中的po打印错误可能是因为它正在打印它,假设它是类型ScriptingEmptyBlock(块类型)。如果您执行po (id) success,则会打印(Function)

关于如何从Swift中明确地将Objective-C块放入数组中,我想出的唯一方法是:

let params:[Any] = [ "testName",
                     successClosure as (@convention(block) () -> Void)!,
                     errorClosure as (@convention(block) (NSError) -> Void)!]
object.scripting_execute (ScriptOperation.updateProductName.rawValue,
                          withParams: params)

我不确定为什么有必要将函数类型放在!中,但它似乎不起作用。也许其他人可以找到更好的方法。