我创建了一个 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:
将其转换为选择器,然后执行调用。
所有这些都有效,除了当调用的方法有 block 参数时它们是 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)
为什么闭包变成了 nil?
最佳答案
success
是notnil
(事实上NSArray
不能包含nil
秒)。如果你像 NSLog(@"%@", success);
这样打印它,它会说 (Function)
,而不是 (null)
。如果你像 NSLog(@"%@", [success class]);
这样打印它的类,它会说 _SwiftValue
。基本上,它是桥接到 Objective-C 的 Swift 值。
问题是 success
指向的对象不是 Objective-C block 。它是一个 Swift 闭包,Swift 闭包与 Objective-C block 不同。尝试像使用 Objective-C block 一样调用它会导致未定义的行为。调试器中的 po
打印错误可能是因为假设它是类型 ScriptingEmptyBlock
( block 类型)。如果你执行po (id) success
,它会打印(Function)
。
关于如何从 Swift 显式地将 Objective-C block 放入数组中,我想出的唯一方法如下:
let params:[Any] = [ "testName",
successClosure as (@convention(block) () -> Void)!,
errorClosure as (@convention(block) (NSError) -> Void)!]
object.scripting_execute (ScriptOperation.updateProductName.rawValue,
withParams: params)
我不确定为什么有必要将函数类型放在 !
中,但它似乎无法正常工作。也许其他人可以找到更好的方法。
关于ios - 数组中的 Swift 闭包在 Objective-c 中变为 nil,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44684601/