gpt4 book ai didi

macOS:根据可用性在运行时加载一个或其他系统框架

转载 作者:行者123 更新时间:2023-12-03 16:09:04 26 4
gpt4 key购买 nike

我正在开发一个使用 Apple 的 Safari 框架的 macOS 工具。在 macOS 10.13 中运行时,该工具会链接到并从中加载

/System/Library/PrivateFrameworks/Safari.framework

一切正常。但在 macOS 10.12.6 中运行时,某些行为会丢失。根据对 DTrace 的一些探测,我认为这是因为我的工具需要加载最新的 Staging 框架,如下所示:

/System/Library/StagedFrameworks/Safari/Safari.framework

这显然是 Safari 所做的,因为如果我使用 lldb 附加到 Safari 并运行image list,则在 10.13 中该列表仅包含前一个路径,而在 10.12.6 中仅包含后者。

我尝试了以下方法:

NSBundle* stagedBundle = [NSBundle bundleWithPath:@"/System/Library/StagedFrameworks/Safari/Safari.framework"];

在 10.13 中返回 nil,因为此时没有这样的目录。但是,在 10.12.6 中,我得到了一个 stagedBundle,然后:

NSBundle* privateBundle = [NSBundle bundleForClass:[BookmarksController class]];
[privateBundle unload];
[stagedBundle load];

卸载和加载显然有效,因为如果我记录这两个包的 -description ,在运行该代码之前,私有(private)包是(已加载)暂存 包是(尚未加载),但运行该代码后,这些状态会根据需要进行交换。

但是没有效果。 (1) 如果我再次调用 -bundleForClass:,传递一个已知在两个框架中的类,它会为我提供 Private 包。 (2) 如果我调用 -respondsToSelector:,传递已知仅存在于 Staging 框架中的选择器,我会得到“否”。

我尝试按照建议调用_CFBundleFlushBundleCaches() here ,但这并没有帮助。

我还尝试更改目标的 FRAMEWORK_SEARCH_PATHS,并在我的 Mac 上安装 Staging 框架并链接到它,但由于这篇文章已经太长了,我'我只能说这产生的热量多于光。

在这种情况下如何有选择地加载框架?

更新

我尝试了另一种方法。重新阅读苹果的Framework Programming Guide后,尽管看起来确实过时了,但我认为这个框架需要弱链接。这样做了:

  • 在代码中,删除了那些 NSBundle -load-unload 调用
  • 在我的工具目标中,
    • 构建阶段> 将二进制文件与库链接,删除了 Safari Private 框架的路径,因为这是一个强链接。
    • build设置> 其他链接器标志中添加了-weak_framework Safari
    • build设置> 框架搜索路径中,列出了两个框架父目录的路径,暂存路径位于之前私有(private)路径,因为我希望在 macOS 10.12.6 中加载该路径,两者都存在。

这对我来说很有意义,在 10.13 和 10.12.6 中构建并运行,但它显然仍在 10.12.6 中加载不需要的私有(private)框架。 NSLog 将其报告为 bundle 的路径,并且类不会响应仅位于 Staging 框架中的选择器。

还有其他想法吗?

最佳答案

首先,免责声明:我强烈建议您不要在交付给用户的任何应用程序中依赖加载私有(private)框架。它很脆弱并且没有支撑。

也就是说,如果您确实想这样做,我的建议是使用 Safari 本身在框架的两个副本之间进行选择的相同技术,即 dyldDYLD_VERSIONED_FRAMEWORK_PATH环境变量。

引用dyld man page :

This is a colon separated list of directories that contain potential override frameworks. The dynamic linker searches these directories for frameworks. For each framework found dyld looks at its LC_ID_DYLIB and gets the current_version and install name. Dyld then looks for the framework at the install name path. Whichever has the larger current_version value will be used in the process whenever a framework with that install name is required. This is similar to DYLD_FRAMEWORK_PATH except instead of always overriding, it only overrides is the supplied framework is newer. Note: dyld does not check the framework's Info.plist to find its version. Dyld only checks the -current_version number supplied when the framework was created.

简而言之,这会导致 dyld 在正在加载的框架和版本化框架路径中的框架之间执行版本检查,并加载更高版本。如果版本化框架路径不存在或者其中不存在相关框架,则将使用原始框架路径。

Safari 使用第二个 dyld 功能来简化其对 DYLD_VERSIONED_FRAMEWORK_PATH(LC_DYLD_ENVIRONMENT 加载命令)的使用。此加载命令允许在链接时指定DYLD_* 环境变量,该变量将由dyld 在运行时尝试加载任何依赖库之前应用。如果没有这个技巧,您需要在启动应用程序之前将 DYLD_VERSIONED_FRAMEWORK_PATH 设置为环境变量,这通常需要繁琐的重新执行才能实现。

将这两个构建 block 放在一起,您最终会添加如下配置设置:

OTHER_LDFLAGS = -Wl,-dyld_env -Wl,DYLD_VERSIONED_FRAMEWORK_PATH=/System/Library/StagedFrameworks/Safari;

然后,您可以静态链接到 /S/L/PrivateFrameworks/Safari.framework,或尝试在运行时动态加载它。两者都应该导致在运行时加载适当的框架。

<小时/>

为了解决您的问题所揭示的一些误解:

The unloading and loading apparently works, because if I log -description of those two bundles, before running that code the Private bundle is (loaded) and the Staged bundle is (not yet loaded), but after running that code those states are swapped, as desired.

不支持卸载包含 Objective-C 代码的共享库。我怀疑它所做的唯一事情是导致在NSBundle实例上切换“已加载”标志,因为在dyld的级别它被忽略。

In Build Settings > Framework Search Paths, listed paths to both frameworks' parent directories, with the Staged path before the Private path, because I want this one to load in macOS 10.12.6, where both exist.

框架搜索路径是一个仅在编译时使用的概念。在运行时,库的安装名称告诉dyld在哪里可以找到要加载的二进制文件。

关于macOS:根据可用性在运行时加载一个或其他系统框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49375324/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com