在Xcode≥7.3中处理私有框架

迈克尔·多纳

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

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

有长期解决方案的想法吗?

内特

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

这是您如何在Github上修改项目的方法

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

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

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

3a)注意:生成的标头有时不会编译。为了使项目得以构建,我不得不struct在上面的Runtime Browser标头注释了几个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"];

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章