Xcode 6和嵌入式框架仅支持iOS8及以上版本。

69

在 Xcode 6.0.1 中使用嵌入式框架(dyld)并且部署目标小于 iOS 8 时,出现以下情况:

  • 构建成功
  • 运行时库加载错误

错误信息:

dyld: Library not loaded: @rpath/ObjectiveLyricsTouch2.framework/ObjectiveLyricsTouch2        
Referenced from: /private/var/mobile/Containers/Bundle/Application/DC65ACA9-98E5-46CD-95F8-829D3416F6C0/musiXmatch.app/musiXmatch
Reason: image not found
(lldb) 

1
是的,您可以在iOS 7上使用动态框架:http://quellish.tumblr.com/post/103107323582/dynamic-frameworks-on-ios-7 - quellish
@quellish 很好知道,我在 WWDC '14 上与苹果工程师交流过。他们没有建议使用这种技术。话虽如此,在某些情况下,这可能是一种可行的解决方法。 - loretoparisi
15个回答

40

有一段时间我认为这也是我的问题,但对于正常的应用程序(非iOS 8扩展),您只需更改Xcode 6 iOS通用框架目标中的一个构建设置(将Mach-O类型设置为静态库):

将其设置为静态库

在那之后,使用iTunes Connect和iOS 7不应该有任何问题 :)


5
在验证时,出现"发现了意外的Mach-O Header代码:一些数字..."的提示。 - Ravi Dalmia
2
我没有进行应用程序审核,但这对我来说一直有效,直到将二进制文件提交到iTunes Connect。 - Anthony
1
与Anthony所说的类似,我也能够通过此方法验证ipa是否符合iTunes Connect的要求。应用程序在iOS 7设备和iOS 8设备上运行,只需进行一些简短的测试即可。回答很好。 - Jonny
@MaciekCzarnik 这对于Swift框架也适用吗? - Lightygalaxy
我不确定Swift @Lightygalaxy,你有检查过吗? - Maciek Czarnik
显示剩余6条评论

30

因此,在寻找解决方案后,我找到了以下答案:

如果要将MyEmbeddedFramework.framework添加到应用程序中,请执行以下操作:

  1. 在选项卡“General > Embedded Binaries”中删除 MyEmbeddedFramework.framework。
  2. 如果“Build Phases”中有MyEmbeddedFramework.framework,请删除复制阶段“Frameworks”。
  3. 清除构建文件夹。
  4. 将MyEmbeddedFramework.framework移动到void Embedded Frameworks部分。
  5. 您现在会发现XCode6创建了一个新的Build Phase > Embedded Frameworks(不是您,而是自动完成的)。
  6. 如果您达到了第5步,则应该可以运行而无需出现错误。

因此,为了使其正常工作,您应在以下位置看到MyEmbeddedFramework.framework:

A) General > Embedded Binaries General > Embedded Binaries

B) Build Phase > Embedded Frameworks Build Phase > Embedded Frameworks

iPhone5/iOS8 上它可以正常工作,但不适用于 iPhone4S/iOS7,在那里我遇到了如下问题:

dyld: Library not loaded: @rpath/ObjectiveLyricsTouch2.framework/ObjectiveLyricsTouch2 Referenced from: /var/mobile/Applications/739D9C44-3B91-4D4F-805B-83BE66C9CBCA/musiXmatch.app/musiXmatch Reason: no suitable image found. Did find:

/private/var/mobile/Applications/739D9C44-3B91-4D4F-805B-83BE66C9CBCA/musiXmatch.app/Frameworks/ObjectiveLyricsTouch2.framework/ObjectiveLyricsTouch2: 不兼容的cpu-subtype: 0x0000000B 在 /private/var/mobile/Applications/739D9C44-3B91-4D4F-805B-83BE66C9CBCA/musiXmatch.app/Frameworks/ObjectiveLyricsTouch2.framework/ObjectiveLyricsTouch2中。
问题出在嵌入式框架上。我需要:
1)将体系结构设置为默认值 2)将有效体系结构设置为:armv7、armv7s和armv64(如苹果建议,需要armv64才能使嵌入式框架正常工作)。
然后我就能在以下设备上运行带有嵌入式框架的应用程序:
  • iPhone5S/iPhone5C iOS8
  • iPhone5S/iPhone5C iOS7
  • iPod 5th gen / iOS7
  • iPhone4S / iOS7
  • iPhone4 / iOS7
但无论如何当提交到iTunesConnect时,我都会得到一些关于最低要求版本的错误:
  • 框架“...”的MinimumOSVersion无效。最小值是iOS 8.0;
  • 无效体系结构:包括应用扩展和框架的应用程序必须支持arm64;

Embedded Framework Issues


12
只是为了明确,您的意思是您的研究结果表明,尽管动态框架可以在iOS 7上运行,但App Store提交验证器将拒绝试图在iOS 7上使用动态框架的应用程序? - Matt Foley
3
是的,这正是我在这里所拥有的,在添加了arm64支持后,当您拥有一个现代的iOS8应用程序,至少包含一个应用程序/小部件插件嵌入式框架(如苹果文档所述)时,iTunes Connect将以那种方式回复。 - loretoparisi
2
我们认为苹果不太可能允许它们在iOS7上运行,因为动态框架的代码签名问题。 - Matt Foley
你能否编辑你的回答,在顶部(而不是底部)加上一条注释,说“这个回答不是一个答案”? - David van Dugteren
1
在Xcode中,“in the void Embedded Frameworks section.”在哪里?我在General下面没有看到。 - xta
显示剩余10条评论

12

目前还没有一种嵌入式框架可以在iOS 8以及iOS 7及之前的版本中共享应用和小部件之间的代码并运行。

关于这方面有更多阅读材料,请参考http://atomicbird.com/blog/ios-app-extension-tips

框架与iOS 7

如果您要在应用和扩展之间共享代码,则创建自己的嵌入式框架来保存代码是一个好方法。在iOS 8上,它会动态加载,因此您可以使用它。

但如果您仍然支持iOS 7(或更早),情况就不那么明确了。嵌入式框架在那里无法工作。应用程序扩展编程指南轻松地注意到您可以使用dlopen来解决此问题。通过该方法,您需要编写代码,在运行时动态加载框架,而不是依靠iOS为您加载它,如果您已经验证代码正在运行支持这样做的iOS版本上。

但是,如何在iOS 7上使用该代码?您不可以。如果您的共享代码位于嵌入式框架中,则无法在iOS 7上执行它。它不可用。

如果您只需要在iOS 8上使用共享代码,则dlopen方法可能会很方便。如果您需要在iOS 7上使用它,则需要将其包含在应用程序目标中。一旦这样做,您就不需要框架了。您仍然可以在应用扩展中使用框架,但这样做实际上并没有任何用处。您将创建框架的工作,但不会从中获得任何好处。只需将共享代码包含在两个目标中即可。

来自苹果的扩展指南,https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensibilityPG.pdf

如果您从包含应用程序链接到一个嵌入式框架,即使在那些不支持嵌入式框架的iOS旧版本上也可以部署该应用程序。


BigCheesy,我已经阅读了dlopen的文档,但是一点也不理解。在iOS7中是否可以使用嵌入式框架? - user1010819
@user1010819 iOS 7 运行时 - 不支持。但如果主应用的部署目标是 iOS7,那么是支持的。 - Mike Glukhov

4

苹果文档中深入了解后,我发现dlopen命令用于根据系统版本和支持的库的条件来进行库的链接。

dlopen使用示例:函数'dlopen()'是私有API吗?

因此,让我们看一下苹果文档提供的解决方案:

将包含应用部署到旧版iOS

如果您从包含应用程序中链接到嵌入式框架,则即使在那些版本中不可用,您仍然可以将其部署到早于8.0的iOS版本中。

这个机制是使用 dlopen 命令,可以有条件地链接和加载框架束来实现的。你可以将这个命令用作在 Xcode GeneralBuild Phases 目标编辑器中指定的构建时链接的替代方案。其主要思想是仅在 iOS 8.0 或更高版本中运行时将嵌入式框架链接到您的容器应用程序中在代码语句中有条件地加载框架束时,必须使用 Objective-C,而不是 Swift。应用程序的其余部分可以使用任何一种语言编写,嵌入式框架本身也可以使用任何一种语言编写。
调用 dlopen 后,可以使用以下类型的语句访问嵌入式框架类:
MyLoadedClass *loadedClass = [[NSClassFromString (@"MyClass") alloc] init];

重要提示

如果您的应用程序包含嵌入式框架链接,它必须包括arm64架构,否则将被App Store拒绝。

设置应用扩展Xcode项目以利用条件链接

  1. 对于每个包含的应用程序扩展,像往常一样将部署目标设置为iOS 8.0或更高版本。在Xcode目标编辑器的“部署信息”部分进行此操作。
  2. 对于您的容器应用程序,请将部署目标设置为您想要支持的最旧版本的iOS。
  3. 在您的容器应用程序中,通过使用systemVersion方法在运行时检查iOS版本来有条件地调用dlopen命令。仅当您的容器应用程序在iOS 8.0或更高版本上运行时才调用dlopen命令。在进行此调用时,请务必使用Objective-C而不是Swift。

Certain iOS APIs use embedded frameworks via the dlopen command. You must conditionalize your use of these APIs just as you do when calling dlopen directly. These APIs are from the CFBundleRef opaque type:
CFBundleGetFunctionPointerForName CFBundleGetFunctionPointersforNames
And from the NSBundle class: load loadAndReturnError: classNamed:
In a containing app you are deploying to versions of iOS older than 8.0, call these APIs only within a runtime check that ensures you are running in iOS 8.0 or newer, and call these APIs using Objective-C.

4

修复了Xcode 6.1.1中的错误。

使用vim或vi打开project.pbxproj文件。

在文件末尾(搜索8.1),会有一个Begin XCBuildConfiguration部分

找到你的框架。

在我们的情况下,即使通过Xcode ->目标设置中将部署目标设置为7.1,文件中的条目也对于调试和发布均为8.1

这是旧文件部分的样子:

CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = ENFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";

新的部分如下所示:
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = ENFramework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";

现在我们只收到一个警告,而不是错误(但在iOS 7.1设备上可以工作): ld: 警告:嵌入的dylibs/frameworks仅在iOS 8或更高版本上运行
这看起来像是Xcode的一个错误,它错误地设置了不同的iOS目标,然后导致了错误。

1
你确认在提交过程中它没有被拒绝吗? - hammett
不起作用,版本在之后也没有正确显示。 - Ravi Dalmia
这是本页面上唯一对我有用的答案。只需将 IPHONEOS_DEPLOYMENT_TARGET 从8.2更改为7.1,保存项目文件,就可以成功构建了。谢谢。 - John Rogers

2
仅供记录...我在将项目从iOS8更改为iOS7部署类型时遇到了此问题。该应用程序使用了cocoapods,没有自定义嵌入式框架。
我必须更改主项目的两个目标
应用
应用-测试
将Mach-O类型更改为静态(来自上面的答案)。
然后,在cocoapods项目中,在每个子pod项目下将Mach-O类型更改为静态,将主pod项目的Mach-O设置留空。

2
我们尝试在以下配置上运行最新的代码:
iOS 8+ - iPhone 5s iOS 7.1.2 - iPhone 4 iOS 6.1.3 - iPad 4
这个应用在这三个设备上都可以正常工作,但是在编译时 Xcode 中出现了警告。 “嵌入的 dylibs/frameworks 只能在 iOS 8 或更高版本上运行”
此外,我尝试将应用程序存档以提交到应用商店,一切都进行得很顺利。
同时,我发现一个链接,其中一个苹果开发人员指出这是一个错误: https://devforums.apple.com/message/999579#999579

虽然当我们使用分发配置文件并尝试将应用提交到itunesconnect时,系统会报出错误:“...'”框架的MinimumOSVersion无效。最小值为iOS 8.0; - Ravi Dalmia

2

我将 Mach-O 类型设置为可执行文件,这对我起了作用。 将其设置为静态、动态或捆绑包会在运行时创建其他错误。

目标 >“您的应用程序”>构建设置>链接> Mach-O 类型> 可执行文件


这个解决方案对我在iOS 9.3.2上也有效。 - DzungPV

1
我是这样解决这个问题的: 在“嵌入式框架”和“主应用程序”目标中使用相同的部署目标。

1

因此,暂时我拒绝使用动态库,尽管在iOS 7上有许多设备可以使用。我是如何解决我的问题的?我需要一个库来在应用和扩展之间传输模型。因此,我将我的模型放入JSON字符串中,并将其放入共享容器中。它运作得非常好。


这是一种很好的选项,可以将对象模型序列化/反序列化,并通过容器应用程序和扩展共享它。问题在于,如果您有一个更复杂的框架并且需要共享逻辑,则可以通过嵌入式扩展二进制文件和应用程序所在的沙箱来共享文件系统文件夹,因此也可以共享整个数据库。 - loretoparisi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接