将闭包传递给Private API

时间:2016-07-01 19:30:19

标签: ios swift selector iphone-privateapi

我正在尝试从私有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)
    }

1 个答案:

答案 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);
}