ios - 将闭包传递给私有(private) API

标签 ios swift selector iphone-privateapi

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

相关文章:

javascript - 使用 jQuery 选择器作为方法

iphone - UIScrollView 问题 : forbid to "scroll to empty space"

javascript - 当用户在文本框外单击时隐藏在 iOS 键盘中

swift - AWS S3 签名的 UploadPart header 的 Content-MD5

jquery - 使用 jQuery 的 iframe

c# - 如何显示所有可能颜色的网格?

ios - "Unable to download App... could not be installed at this time"- adobe Phonegap 构建 ios

具有透明背景的 iOS 应用程序图标在设备上显示黑色背景

objective-c - 将 0 作为枚举选项传递给 Swift 中的 Objective C 函数

ios - NavigationBar 在动画期间改变他的颜色