2016-07-01 5 views
0

プライベートAPI MPAVRoutingControllerから利用可能なすべてのエアプレイデバイスを取得しようとしています。私はperformSelector-Swiftと呼ばれるswiftのサードパーティのperformセレクタライブラリを使用しています。私が呼び出そうとしているメソッドはfetchAvailableRoutesWithCompletionHandlerです。これは1つのパラメータ、objective-cブロックを取ります。 - (void)fetchAvailableRoutesWithCompletionHandler:(id /* block */)arg1;私がクロージャを試して渡すと、コンパイルエラーが発生し、私のアプリで何かを渡さないとクラッシュします。私はこのアプリを公開していないと私はなぜpriv APIを使用しているthatsだ。 http://i.imgur.com/UGVayPE.pngプライベートAPIにクロージャを渡す

それはそれを呼び出す完了ブロックへのパラメータとしてNSMutableArrayを割り当てていることを示しています。私は正しい完了ブロックの署名を発見した方法

let MPAVRoutingController = NSClassFromString("MPAVRoutingController")! as! NSObject.Type 
let routingController = MPAVRoutingController.init() 
if let availableRoutes = routingController.swift_performSelector("fetchAvailableRoutesWithCompletionHandler:", withObject: { 
     object in 
    }) { 
     print(availableRoutes) 
    } 
+0

要求されたクロージャのシグネチャ、つまり「オブジェクト」は間違いなく正しいのですか?プライベート関数は必ず何かのためにクロージャーを使用し、別のパラメーターを渡そうとするとエラーが発生します。また、swift_performSelectorはオブジェクトパラメータのクロージャも受け入れますか?その第三者のlibの医者はそれについて何を言っていますか? – Gero

+0

申し訳ありません私はメソッドシグネチャを貼り付けたと思ったが、私は何か他のものを貼り付けた。 sigは、リバースエンジニアリングヘッダファイル(https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks)に従って、 ' - (void)fetchAvailableRoutesWithCompletionHandler :(id/* block * /)arg1;'です。 /MediaPlayer.framework/MPAVRoutingController.h)。ライブラリについては、私はちょうどそれをスクラップし、Obj-Cのアプリケーションのこのセクションを書き、後でそれをブリッジする –

+0

ダング、それはそれを明確にしていない、重要な部分がコメントアウトされる原因になります。しかし、実際に '(^)(parameterTypes) '部分を削除したとすると、少なくとも' id'(つまり、 'AnyObject'を即座に返す)のように見えるように見えます。私はヘッダーを次のように理解しています: "ブロックに' id'を返して渡してください。だから多分 '/ * ... */withObject:{() - > AnyObject in/*はダミーオブジェクトを定義し、それを返す/ *})' 'と試してみてください。通常、設定するパラメータを正確に知らなくても、リバースエンジニアリングされたメソッドを呼び出すのは、少なくとも私の経験からは、すぐに南下します。 – Gero

答えて

2

まず..これが唯一のパラメータです。あなたはこれを行う必要はありません(それを分解する)。例外がスローされると、署名を印刷できます。場合によっては、どのブロックが予想されているかを示すこともあります。


次に、動的セレクタを起動に関する私の意見を...

あなたの最良のオプションは、セレクタを行わないように..ですこれは、呼び出しが複数のパラメータが含まれている場合は特に痛みだ...

何のことができます。私はC++(Pimplイディオムからのアイデア.. COMMインターフェイスもこれをやっています)でこれを行い、Swift、Objective-C、Javaなどで動作します。

同じインターフェイスを持つプロトコルを作成するeをオブジェクトとして使用します。そのプロトコルを継承する拡張を作成します。次に、オブジェクトインスタンスをその拡張/インタフェース/プロトコルにキャストします。

インターフェイス/拡張/プロトコルポインタを使用して、必要な機能を呼び出します。

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. 
} 

使用法:

#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: MPAProtocol = MPAVRoutingControllerClass.init() as MPAProtocol 

MPAVRoutingController.fetchAvailableRoutesWithCompletionHandler! { (routes) in 
    print(routes); 
} 

は、あなたが単一のObjective-Cのカテゴリを行うだけだろうその後

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); 
} 
+1

ええ、これはちょうど考えていた。ありがとう! –

関連する問題