可以通过链接私有框架来解决这个问题动态地,而不是更常见的链接方式构建时间。在构建时,BluetoothManager.framework 需要存在于您的开发 Mac 上,以便链接器能够使用它。通过动态链接,您可以将进程推迟到运行时。在设备上,iOS 9.3 仍然存在该框架(当然还有其他框架)。
以下是您可以修改项目的方法Github https://github.com/michaeldorner/BeeTee:
1) 在 Xcode 的 Project Navigator 中的 Frameworks 下,删除对 BluetoothManager.framework 的引用。无论如何,它可能显示为红色(未找到)。
2)项目下构建设置,您将旧的私有框架目录明确列为框架搜索路径。删除它。如果您找不到“PrivateFrameworks”,请在构建设置中搜索它。
3) 确保添加您需要的实际标头,以便编译器理解这些私有类。我相信你可以获得当前的标题例如这里 https://github.com/nst/iOS-Runtime-Headers/tree/master/PrivateFrameworks/BluetoothManager.framework。即使框架从 Mac SDK 中删除,我相信这个人也使用过类似的工具运行时浏览器 https://github.com/nst/RuntimeBrowser在设备上生成头文件。根据您的情况,将BluetoothManager.h 和BluetoothDevice.h 标头添加到Xcode 项目中。
3a) Note:生成的标头有时无法编译。我不得不评论一些struct
上面的 typedef运行时浏览器标头 https://github.com/nst/iOS-Runtime-Headers/tree/master/PrivateFrameworks/BluetoothManager.framework为了让项目建成。下面是哈蒂普@Alan_s。
4) 更改您的导入:
#import <BluetoothManager/BluetoothManager.h>
to
#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
在您的单例方法中,但您可以将其放在其他地方。只需要调用它before任何代码都使用私有 API 类。
我添加了一个方便的方法[MDBluetoothManager bluetoothManagerSharedInstance]
因为你会反复调用它。当然,我确信您可以找到替代的实现。重要的细节是这个新方法使用动态实例化私有类NSClassFromString()
.
6) 任何你直接调用的地方[BluetoothManager sharedInstance]
,将其替换为新的[MDBluetoothManager bluetoothManagerSharedInstance]
call.
我使用 Xcode 7.3 / iOS 9.3 SDK 对此进行了测试,您的项目在我的 iPhone 上运行良好。
Update
由于似乎存在一些混乱,因此相同的技术(和确切的代码)仍然适用于 iOS 10.0-11.1(截至撰写本文时)。
此外,强制加载框架的另一个选项是使用[NSBundle bundleWithPath:]
代替dlopen()
。但请注意路径上的细微差别:
handle = dlopen("/System/Library/PrivateFrameworks/BluetoothManager.framework/BluetoothManager", RTLD_NOW);
NSBundle *bt = [NSBundle bundleWithPath: @"/System/Library/PrivateFrameworks/BluetoothManager.framework"];