处理Xcode中的私有框架≥7.3

Mic*_*ner 16 xcode iphone-privateapi ios ios9.3

使用Xcode 7.3/iOS 9.3 Apple从iOS SDK中删除了所有私有框架.出于研究目的(不是App Store!),我需要使用私有框架(即BluetoothManager.framework,这也是任何其他私有框架的问题).

因为这些框架不再在iOS SDK中提供,所以如果我的项目尝试显式链接到此框架,则会出现构建(链接器)错误.

对于长期(呃) - 期限解决方案的任何想法?

Nat*_*ate 23

您可以通过动态链接到私有框架来解决此问题,而不是在构建时链接更常见的方式.在构建时,BluetoothManager.framework需要存在于您的开发Mac上,以便链接器能够使用它.通过动态链接,您可以将流程推迟到运行时.在设备上,iOS 9.3仍然存在该框架(当然也包括其他框架).

以下是如何在Github上修改项目:

1)在Xcode的Project Navigator中,在Frameworks下,删除对BluetoothManager.framework的引用.无论如何,它可能显示为红色(未找到).

2)在项目构建设置下,您将旧的私有框架目录明确列为框架搜索路径.删除它.如果您在查找时遇到问题,请在构建设置中搜索"PrivateFrameworks".

3)确保添加所需的实际头文件,以便编译器理解这些私有类.我相信你可以在这里得到当前的标题.即使从Mac SDK中删除了框架,我相信这个人在设备上使用了像Runtime Browser这样的工具来生成头文件.在您的情况下,将BluetoothManager.h和BluetoothDevice.h标头添加到Xcode项目中.

3a)注意:生成的头文件有时不编译.我必须struct在上面的运行时浏览器标题中注释掉几个typedef 才能生成项目.Hattip @Alan_s下面.

4)更改您的进口:

#import <BluetoothManager/BluetoothManager.h>
Run Code Online (Sandbox Code Playgroud)

#import "BluetoothManager.h"
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

我把调用dlopen放在你的单例方法中,但是你可以把它放在其他地方.只需任何代码使用私有API类之前调用它.

我添加了一种方便的方法,[MDBluetoothManager bluetoothManagerSharedInstance]因为你会反复调用它.当然,我确信你可以找到其他的实现方式.重要的细节是这个新方法使用动态实例化私有类NSClassFromString().

6)您直接呼叫的任何地方[BluetoothManager sharedInstance],用新[MDBluetoothManager bluetoothManagerSharedInstance]呼叫替换它.

我使用Xcode 7.3/iOS 9.3 SDK进行了测试,您的项目在我的iPhone上正常运行.

更新

由于似乎存在一些混淆,因此相同的技术(和确切的代码)仍然适用于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"];
Run Code Online (Sandbox Code Playgroud)