Подтвердить что ты не робот

Обработка частных фреймворков в Xcode ≥ 7.3

С Xcode 7.3/iOS 9.3 Apple удалила все частные фреймворки из SDK iOS. Для исследовательских целей (а не в App Store!) Мне нужно работать с частной инфраструктурой (а именно BluetoothManager.framework, но это также проблема для любых других частных фреймворков).

Поскольку эти фреймворки больше не доставляются в SDK iOS, я получаю ошибку сборки (компоновщика), если мой проект явно ссылается на эту структуру.

Любые идеи для длинного (er) -термического решения?

4b9b3361

Ответ 1

Вы можете решить эту проблему, связав ее с частной инфраструктурой динамически, а не более общим способом связывания во время сборки. Во время сборки BluetoothManager.framework должен существовать на вашем Mac разработки, чтобы компоновщик мог его использовать. При динамической компоновке вы откладываете процесс до времени выполнения. На устройстве iOS 9.3 все еще имеет эту структуру (и, конечно же, другие).

Вот как вы можете изменить свой проект на Github:

1) В Навигаторе проектов Xcode в рамках Frameworks удалите ссылку на BluetoothManager.framework. Вероятно, это было красным (не найдено).

2) В проекте Настройки сборки у вас есть старый каталог private framework, явно указанный в качестве пути поиска в каркасе. Удалите это. Найдите "PrivateFrameworks" в настройках сборки, если у вас есть проблемы с поиском.

3) Обязательно добавьте нужные заголовки, поэтому компилятор понимает эти частные классы. Я считаю, что вы можете получить текущие заголовки здесь, например,. Даже если рамки удалены из Mac SDK, я считаю, что этот человек использовал инструмент, например заголовках обозревателя Runtime, чтобы получить проект для сборки. Hattip @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 в ваш метод singleton, но вы могли бы поместить его в другое место. Его просто нужно называть до, любой код использует частные классы API.

Я добавил метод удобства [MDBluetoothManager bluetoothManagerSharedInstance], потому что вы будете называть это повторно. Конечно, вы можете найти альтернативные реализации, конечно. Важная деталь заключается в том, что этот новый метод динамически создает экземпляр частного класса с помощью NSClassFromString().

6) Всюду, где вы прямо звонили [BluetoothManager sharedInstance], замените его новым вызовом [MDBluetoothManager bluetoothManagerSharedInstance].

Я тестировал это с помощью Xcode 7.3/iOS 9.3 SDK, и ваш проект отлично работает на моем iPhone.

Update

Так как существует некоторая путаница, эта же техника (и точный код) по-прежнему работает в iOS 10.0-10.1.