我正在尝试从私有(private) API MPAVRoutingController 获取所有可用的 airplay 设备。我正在为 swift 使用名为 performSelector-Swift 的第三方执行选择器库。我尝试调用的方法是 fetchAvailableRoutesWithCompletionHandler。这需要一个参数,一个 objective-c block 。 - (void)fetchAvailableRoutesWithCompletionHandler:(id/* block */)arg1;
当我尝试传入闭包时出现编译错误,如果我没有在应用程序中传递任何内容,我的应用程序就会崩溃。我不会发布此应用程序,这就是我使用 priv API 的原因。
let MPAVRoutingController = NSClassFromString("MPAVRoutingController")! as! NSObject.Type
let routingController = MPAVRoutingController.init()
if let availableRoutes = routingController.swift_performSelector("fetchAvailableRoutesWithCompletionHandler:", withObject: {
object in
}) {
print(availableRoutes)
}
最佳答案
首先..我是如何找到正确的完成 block 签名的:http://i.imgur.com/UGVayPE.png
这表明它在调用它时分配了一个 NSMutableArray
作为完成 block 的参数。那是唯一的参数。您不必执行此操作(将其拆解)。抛出异常时,您可以打印签名。有时它还会告诉您需要哪种类型的 block 。
接下来,我对动态调用选择器的看法..
您最好的选择是不执行选择器。这很痛苦,尤其是当调用包含多个参数时。
你可以做的是通过接口(interface)/扩展指针调用。我一直在用 C++(来自 Pimpl 惯用语的想法。COMM 接口(interface)也这样做)来做这个,它可以与 Swift、Objective-C、Java 一起工作。 .等..
创建一个与对象具有相同接口(interface)的协议(protocol)。创建一个继承该协议(protocol)的扩展。然后将对象实例转换为该扩展/接口(interface)/协议(protocol)。
通过接口(interface)/扩展/协议(protocol)指针调用你想要的任何函数。
import UIKit
import MediaPlayer
@objc
protocol MPAProtocol { //Functions must be optional. That way you don't implement their body when you create the extension.
optional func availableRoutes() -> NSArray
optional func discoveryMode() -> Int
optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: NSArray) -> Void)
optional func name() -> NSString
}
extension NSObject : MPAProtocol { //Needed otherwise casting will fail!
//Do NOT implement the body of the functions from the protocol.
}
用法:
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type
let MPAVRoutingController: MPAProtocol = MPAVRoutingControllerClass.init() as MPAProtocol
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}
如果您要使用桥接 header 而不是创建扩展 + 协议(protocol)来执行此操作,那么您只需执行一个 Objective-C 类别:
#import <Foundation/Foundation.h>
@interface NSObject (MPAVRoutingControllerProtocol)
- (void)fetchAvailableRoutesWithCompletionHandler:(void(^)(NSArray *routes))completion;
@end
@implementation NSObject (MPAVRoutingControllerProtocol)
@end
然后:
let MPAVRoutingControllerClass: NSObject.Type = NSClassFromString("MPAVRoutingController") as! NSObject.Type
let MPAVRoutingController = MPAVRoutingControllerClass.init()
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}
最后,如果你可以使用协议(protocol)注入(inject),你可以更容易地做到这一点:
func classFromString(cls: String, interface: Protocol?) -> NSObject.Type? {
guard let interface = interface else {
return NSClassFromString(cls) as? NSObject.Type
}
if let cls = NSClassFromString(cls) {
if class_conformsToProtocol(cls, interface) {
return cls as? NSObject.Type
}
if class_addProtocol(cls, interface) {
return cls as? NSObject.Type
}
}
return nil
}
func instanceFromString<T>(cls: String, interface: Protocol?) -> T? {
return classFromString(cls, interface: interface)?.init() as? T
}
@objc
protocol MPAProtocol {
optional func availableRoutes() -> NSArray
optional func discoveryMode() -> Int
optional func fetchAvailableRoutesWithCompletionHandler(completion: (routes: NSArray) -> Void)
optional func name() -> NSString
}
let MPAVRoutingController: MPAProtocol = instanceFromString("MPAVRoutingController", interface: MPAProtocol.self)!
MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in
print(routes);
}
关于ios - 将闭包传递给私有(private) API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38152763/