夫特封闭件成为零目标C

问题描述:

我创建一个Objective-C方法,该方法将通过NSInvocation的调用方法:夫特封闭件成为零目标C

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:在子类中,然后执行调用。

所有这些工作,除了当被调用的方法有块参数他们是零,我添加了测试为零我说与评论,当试图从数组中读闭,它将是零。

调用,如:

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) 

为什么封成为零?

我必须承认,我不完全明白为什么会发生这种情况,但据我所知,这与使用NSInvocation无关,即使我们只是将Swift闭包传递给Objective-C功能通过id类型的参数。通过id传递Objective-C块的工作很好,不知道为什么:Swift闭包应该与Objective-C块兼容。如您所知,NSArray的元素类型为id,因此任何Objective-C对象都可以是数组元素。

要解决访问在Objective-C中通过id传递的Swift闭包的问题,​​可以引入一个包装类。

// In a header: 
@interface EmptyBlockWrapper : NSObject 
@property EmptyBlock _blk; 
@end 

// In an implementation file (just an empty implementation): 
@implementation EmptyBlockWrapper 

@end 

然后,我们可以使用一个包装实例的代替块作为在夫特数组元素:

let myBlock : EmptyBlock = { 
    print("In Swift EmptyBlock...") 
} 

let myBlockWrapper = EmptyBlockWrapper() 
myBlockWrapper._blk = myBlock 

在一个Objective-C方法我们可以称之为如下,假设argsNSArray *

EmptyBlockWrapper * emptyBlockWrapper = args[1]; 
emptyBlockWrapper._blk(); 

希望这会有所帮助。当然,这只是给你一个想法的简单例子;这可能会变得更加奇特。

successnil(事实上,NSArray不能包含nil多个)。如果你打印它像NSLog(@"%@", success);,它会说(Function),而不是(null)。如果您打印类似NSLog(@"%@", [success class]);,它会说_SwiftValue。基本上,它是一个桥接到Objective-C中的Swift值。

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

至于你怎么能明确提出一个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) 

我不知道为什么有必要把!中的函数类型,但它似乎不起作用。也许别人可以找到更好的方法。