处理Xcode中的私有框架≥7.3

时间:2016-05-03 09:09:50

标签: ios xcode iphone-privateapi ios9.3

来自iOS SDK的

With Xcode 7.3 / iOS 9.3 Apple removed all private frameworks。出于研究目的(不是App Store!),我需要使用私有框架(即BluetoothManager.framework,但这也是任何其他私有框架的问题)。

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

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

1 个答案:

答案 0 :(得分:22)

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

以下是您在Github上修改项目的方法:

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

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

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

3a)注意:生成的标题有时无法编译。我必须在上面的Runtime Browser headers中注释掉几个struct typedef才能生成项目。 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,但你可以把它放在其他地方。它只需要在之前被称为,任何代码都使用私有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"];