我正在尝试从私有API MPAVRoutingController中获取所有可用的airplay设备。我正在为swift调用performSelector-Swift使用第三方执行选择器库。我试图调用的方法是fetchAvailableRoutesWithCompletionHandler。这需要一个参数,一个objective-c块。 - (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)
}
答案 0 :(得分:3)
首先..我如何找到正确的完成块签名:http://i.imgur.com/UGVayPE.png
这表明它在调用它时将NSMutableArray
作为参数分配给完成块。这是唯一的参数。您不必这样做(拆卸它)。抛出异常后,您可以打印签名。有时它也会告诉你预期哪种块。
接下来,我对动态调用选择器的意见..
您最好的选择是不执行选择器..特别是当调用包含MULTIPLE参数时,这很痛苦。
你可以做的是通过接口/扩展指针调用..我在C ++(来自Pimpl习语的想法...... COMM接口也这样做)中这样做,它适用于Swift,Objective-C,Java。等等。
创建与对象具有相同接口的协议。创建一个继承该协议的扩展。然后将对象实例强制转换为该扩展/接口/协议。
通过接口/扩展/协议指针调用您想要的任何功能。
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);
}
如果您使用Bridging标头而不是创建扩展+协议,那么您只需执行一个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);
}
最后,如果你可以使用协议注入,你可以更容易地做到这一点:
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);
}