web-dev-qa-db-ja.com

Xcode≥7.3でのプライベートフレームワークの処理

Xcode 7.3/iOS9.3ではAppleすべてのプライベートフレームワークを削除 iOS SDKから。調査目的(App Storeではありません!)では、プライベートフレームワークを使用する必要があります(つまり、BluetoothManager.frameworkですが、これは他のprivateフレームワークの問題でもあります。

これらのフレームワークはiOSSDKで提供されなくなったため、プロジェクトがこのフレームワークに明示的にリンクしようとすると、ビルド(リンカー)エラーが発生します。

長期的な解決策について何かアイデアはありますか?

16
Michael Dorner

ビルド時でリンクするより一般的な方法の代わりに、プライベートフレームワーク動的にリンクすることで、この問題を解決できます。ビルド時に、リンカーがBluetoothManager.frameworkを使用できるようにするには、開発用MacにBluetoothManager.frameworkが存在する必要があります。ダイナミックリンクを使用すると、実行時までプロセスを延期できます。デバイス上では、iOS 9.3にはまだそのフレームワークが存在します(もちろん、他のフレームワークも存在します)。

Github でプロジェクトを変更する方法は次のとおりです。

1)Xcodeのプロジェクトナビゲータの[フレームワーク]で、BluetoothManager.frameworkへの参照を削除します。とにかく、おそらく赤で表示されていました(見つかりませんでした)。

2)プロジェクトビルド設定の下に、フレームワーク検索パスとして明示的にリストされた古いプライベートフレームワークディレクトリがあります。それを削除します。見つからない場合は、ビルド設定で「PrivateFrameworks」を検索してください。

3)コンパイラがこれらのプライベートクラスを理解できるように、必要な実際のヘッダーを必ず追加してください。私はあなたが現在のヘッダーを得ることができると信じています 例えばここで 。フレームワークがMacSDKから削除されたとしても、この人はデバイス上で ランタイムブラウザ のようなツールを使用してヘッダーファイルを生成したと思います。あなたの場合、BluetoothManager.hおよびBluetoothDevice.hヘッダーをXcodeプロジェクトに追加します。

3a):生成されたヘッダーがコンパイルされない場合があります。プロジェクトをビルドするために、上記のstruct typedefをいくつかコメントアウトする必要がありました ランタイムブラウザヘッダー 。以下のハットチップ@Alan_s。

4)インポートを次の場所から変更します。

_#import <BluetoothManager/BluetoothManager.h>
_

_#import "BluetoothManager.h"
_

5)プライベートクラスを使用する場合、最初にフレームワークを動的に開く必要があります。これを行うには、(MDBluetoothManager.mで)次を使用します。

_#import <dlfcn.h>

static void *libHandle;

// A CONVENIENCE FUNCTION FOR INSTANTIATING THIS CLASS DYNAMICALLY
+ (BluetoothManager*) bluetoothManagerSharedInstance {
   Class bm = NSClassFromString(@"BluetoothManager");
   return [bm sharedInstance];
}

+ (MDBluetoothManager*)sharedInstance
{
   static MDBluetoothManager* bluetoothManager = nil;
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
      // ADDED CODE BELOW
      libHandle = dlopen("/System/Library/PrivateFrameworks/BluetoothManager.framework/BluetoothManager", RTLD_NOW);
      BluetoothManager* bm = [MDBluetoothManager bluetoothManagerSharedInstance];
      // ADDED CODE ABOVE
      bluetoothManager = [[MDBluetoothManager alloc] init];
   });
   return bluetoothManager;
}
_

シングルトンメソッドでdlopenを呼び出しましたが、他の場所に置くこともできます。コードがプライベートAPIクラスを使用する前に呼び出す必要があります

繰り返し呼び出すので、便利なメソッド_[MDBluetoothManager bluetoothManagerSharedInstance]_を追加しました。もちろん、別の実装を見つけることができると確信しています。重要な詳細は、この新しいメソッドがNSClassFromString()を使用してプライベートクラスを動的にインスタンス化することです。

6)_[BluetoothManager sharedInstance]_を直接呼び出していた場所はすべて、新しい_[MDBluetoothManager bluetoothManagerSharedInstance]_呼び出しに置き換えます。

これをXcode7.3/iOS 9.3 SDKでテストしたところ、プロジェクトはiPhoneで正常に実行されます。

更新

多少の混乱があるように思われるので、これと同じ手法(および正確なコード)はiOS 10.0-11.1でも機能します(この記事の執筆時点)。

また、フレームワークのロードを強制する別のオプションは、dlopen()の代わりに_[NSBundle bundleWithPath:]_を使用することです。ただし、パスのわずかな違いに注意してください。

_handle = dlopen("/System/Library/PrivateFrameworks/BluetoothManager.framework/BluetoothManager", RTLD_NOW);
NSBundle *bt = [NSBundle bundleWithPath: @"/System/Library/PrivateFrameworks/BluetoothManager.framework"];
_
23
Nate