50

我正在尝试为 iOS 应用程序制作一个动态框架。感谢新版本的 Xcode (6),我们可以在创建新项目时选择 Cocoa Touch Framework,不再需要添加聚合目标、运行脚本等来制作。构建框架时我没有问题。但是当我尝试在 iOS 应用程序中使用它时,我遇到了一些架构问题。

ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (arm64): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

ld: warning: ignoring file /Library/Frameworks/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /Library/Frameworks/MyFramework.framework/MyFramework
Undefined symbols for architecture armv7:
  "_OBJC_CLASS_$_MyFrameworkWebService", referenced from:
      objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

好吧,我尝试更改框架项目和目标的设置(架构和仅构建有效架构和有效架构)。我为 iOS 应用程序项目做了同样的事情,但没有任何效果。我觉得有些东西我不明白。

例如,当我使用命令行“xcrun lipo -info MyFramework”为仅 i386(iOS 模拟器)检查构建框架时,我遇到的问题是

ld:警告:忽略文件/Library/Frameworks/MyFramework.framework/MyFramework,文件是为x86_64构建的,它不是被链接的架构(i386)......

如果有人可以帮助我获得适用于所有 iOS 架构(包括模拟器)的框架。

4

10 回答 10

69

根据所有回复、raywenderlich.com 上的帖子Chris Conway创建的要点,我想出了这个

执行以下步骤,我能够构建一个包含模拟器和设备的所有架构的 Cocoa Touch 框架(包括 Swift 和 Objective-C 文件):

  1. 在框架的项目中创建一个新的(聚合)目标
  2. 在“构建阶段”下选择“添加运行脚本”并复制该文件的内容
  3. 方案选择下拉列表中选择聚合目标
  4. 聚合方案构建目标 _

希望能帮助到你 :)

更新:修复了步骤 #3 中的路径不正确的要点中的错误。感谢德陆!!

更新 在 Xcode 7 和 8 中,单击 File>New>Target... 并选择“Other”组以选择 Aggregate target

于 2014-11-01T16:02:36.403 回答
47

尝试以下步骤来创建一个包含框架项目和应用项目的工作区。

工作区:

  • 创建工作区。

框架项目:

  • 在 Workspace 中创建一个 iOS Cocoa touch Framework项目。
  • 添加一个简单的 Objective C 类MyClass(头文件 .h 和实现文件 .m),并创建一个方法- (void)greetings
  • 转到项目Build Phases > Headers > Move MyClass.h from Project to Public
  • 将方案更改为框架项目并选择iOS 模拟器,然后构建。(如果集成此框架的应用程序在设备上运行,请选择iOS设备。在这个例子中我将继续使用模拟器)
  • 您应该没有构建问题,框架构建位于您的Derived Data目录中,您可以在Organizer中找到该目录。

应用项目:

  • 在 Workspace 中创建一个 Swift Single View 应用程序。
  • 将上面的 iOS 模拟器框架构建(在Debug-iphonesimulatorRelease-iphonesimulator中找到)拖到您的项目中。
  • 创建一个桥接头向 Swift 公开 Objective C 类方法。
  • 在桥接头文件中导入 MyClass.h。
  • 请注意,如果未找到 MyClass 定义,则将框架Headers路径添加到 Build Settings Header Search Paths
  • 在ViewController.swift的 viewDidLoad 中创建MyClass的实例,然后调用greetings
  • 将框架添加到 Target > Embedded Binaries
  • 将方案更改为应用程序项目并选择iOS 模拟器,然后构建。
  • 您应该能够看到问候消息。

请注意,上面的示例演示了如何构建在模拟器中运行的应用程序。如果您需要创建同时在模拟器设备上运行的通用静态库,那么一般步骤是:

  • 为模拟器构建库
  • 为设备构建库
  • 使用lipo将它们组合起来

网上有很好的参考资料,例如这里。

为框架创建通用二进制文件:导航到框架派生数据目录,然后/Build/Products ,以下命令应该可以帮助您在Products目录中创建通用二进制文件:

lipo -create -output "framework-test-01-universal" "Debug-iphonesimulator/framework-test-01.framework/framework-test-01" "Debug-iphoneos/framework-test-01.framework/framework-test-01" 

请注意,framework-test-01是我的框架项目名称。

于 2014-06-18T21:14:50.557 回答
10

我这样做的方式类似于 vladof,但希望更简单一些。我使框架成为应用程序项目的子项目。

框架项目

  • 创建一个新的 iOS Cocoa Touch 框架。称它为 MyLib。这将创建一个 MyLib.h
  • 添加一个简单的 Cocoa Touch Obj-C 类 MyClass (.h & .m) 并在 .m 的实现中创建一个返回字符串的方法 - (NSString *)greetings;
  • 在 MyLib.h 中,在底部附近添加 #import "MyClass.h"
  • 在目标的 Build Phases/Headers 部分中,将 MyClass.h 从 Project 部分移动到 Public 部分。
  • 进行构建 (cmd-B)
  • 关闭项目

应用项目

  • 创建一个新的 Single View 应用程序项目,Swift 或 Obj-C。称之为 MyApp。
  • 从 Finder 中,将您的 MyLib 项目文件拖到 MyApp 窗口的左侧管理器部分,并确保插入行正好位于 MyApp 下方。这使得 MyLib 成为 MyApp 的子项目。(在其他项目中仍然可以使用相同的方式)
  • 单击管理器中的 MyApp,然后选择 MyApp 目标并选择 Build Phases。
  • 在 Target Dependancies 中,单击 + 号并添加 MyLib。
  • 在 Link with Libraries 中,单击 + 号并添加 MyLib.framework。

对于 Obj-C 应用程序

  • 在 ViewController.m 中,添加#import
  • 在 viewDidLoad 中,添加以下行:
  • MyLib *x = [[MyLib alloc] init];
  • NSLog(@"%@", x.greetings);
  • 运行项目,您应该会在调试窗口中看到该消息。-

对于 Swift 应用程序

  • 在 ViewController.swift 中,添加 import MyLib
  • 在 viewDidLoad 中,添加以下行:
  • var x: MyLib = MyLib()
  • println("(x.greetings())") -

通过这种方式,应用程序依赖于框架,因此如果您在框架类中进行更改,您不必更改目标来单独构建框架,它只会先编译框架,然后再编译应用程序。

于 2014-06-24T17:39:14.730 回答
7

我稍微改变了某人的脚本以支持所有 Simulator 的架构:

UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal

# make sure the output directory exists
mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"

# Step 1. Build Device and Simulator versions
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator VALID_ARCHS="x86_64 i386" BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

# Step 2. Copy the framework structure to the universal folder
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"

# Step 3. Create universal binary file using lipo and place the combined executable in the copied framework directory
lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"

# Step 4. Convenience step to copy the framework to the project's directory
cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"

# Step 5. Delete temporary build directory in the project's directory
rm -rf "${PROJECT_DIR}/build"

# Step 6. Convenience step to open the project's directory in Finder
open "${PROJECT_DIR}"
于 2015-07-10T20:21:08.683 回答
2

自从之前的上述链接以来,我制作的旧视频不再可用。

如何使用 Xcode 6 创建 Cocoa Touch 框架

于 2014-12-02T10:15:37.527 回答
1

围绕 vladof 共享的方法的一些附加点更适用于基于 swift 的框架

  1. 相关链接中的脚本需要修改以从 iphonesimulator 和 iphoneos 复制所有文件,因为 swift 具有单独的 arm 和 i386 编译文件
  2. 确保您在 iphonesimulator 的构建中有 ARCHS="x86_64" ONLY_ACTIVE_ARCH=NO 以避免模拟器的链接器问题
  3. 确保您的接口/类扩展 NSObject 否则当您尝试在 swift 中使用代码时会遇到问题(它会抱怨无法使用 () 创建对象。
于 2014-07-06T15:50:28.177 回答
1

最后,我让它为我工作!对不起大黄框,我不知道如何更好地格式化它。

解决方案来自 Claudio Romandi,但链接的脚本存在一个小问题。我无法评论他的帖子,因为我需要 50 声望,所以我别无选择,只能复制他的帖子以获得完整的解决方案。

  1. 在您的框架项目中创建一个新的(聚合)目标
  2. 在 Workspace 中选择 Aggregate 并添加“New Run Script Phase”
  3. 将以下代码放入其中:

    #!/bin/sh
    
    UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal
    
    # make sure the output directory exists mkdir -p "${UNIVERSAL_OUTPUTFOLDER}"
    
    # Step 1. Build Device and Simulator versions xcodebuild -target "${PROJECT_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION}
    -sdk iphoneos  BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build xcodebuild -target "${PROJECT_NAME}" -configuration ${CONFIGURATION} -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
    
    # Step 2. Copy the framework structure (from iphoneos build) to the universal folder cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework" "${UNIVERSAL_OUTPUTFOLDER}/"
    
    # Step 3. Copy Swift modules (from iphonesimulator build) to the copied framework directory cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule/." "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/Modules/${PROJECT_NAME}.swiftmodule"
    
    # Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory lipo -create
    -output "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework/${PROJECT_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework/${PROJECT_NAME}"
    
    # Step 5. Convenience step to copy the framework to the project's directory cp -R "${UNIVERSAL_OUTPUTFOLDER}/${PROJECT_NAME}.framework" "${PROJECT_DIR}"
    
    # Step 6. Convenience step to open the project's directory in Finder open "${PROJECT_DIR}"
    
  4. 在方案选择下拉列表中选择聚合

  5. 构建,完成!

问题是模拟器目录指向一个不存在的目录,在两个地方将“框架”更改为“$ {PROJECT_NAME}”就可以了:)

于 2015-01-17T16:02:45.060 回答
1

想在此处由 skywinder 提供的 lipo 脚本答案中添加一些内容 。我按照这些步骤操作,但仍然无法让我的框架在模拟器上运行,只能在设备上运行。要解决这个问题:

-我进入了框架的debug-iphonesimulator版本,在Modules/[FrameworkName].swiftmodule中,我复制了所有的i386和X86文件。

- 然后我进入新创建的框架的胖版本,并导航到同一个文件夹。我粘贴了 i386 和 X86 文件(与那里已经存在的 ARM 文件一起使用),然后将我的胖框架添加到我的项目中。

瞧,她在模拟器和真实设备上工作!这是 Xcode 10 / Swift 4.2 顺便说一句

于 2018-10-25T18:31:58.573 回答
1

这个问题是不久前发布的(Xcode 6),但我最近在使用 Xcode 10 时遇到了同样的问题。

所以问题是:构建的框架不支持足够的架构。

如果你不知道架构代表什么,不同的 iPhone 设备有不同的架构,这里有一个完整的列表:

  • arm6:iPhone 1、2、3G
  • arm7:用于最古老的支持 iOS 7 的设备[32 位]。iPhone 4、4s
  • arm7s:在 iPhone 5 和 5C[32 位] 中使用。iPhone 5、5c
  • arm64:适用于 iPhone 5S[64 位] 中的 64 位 ARM 处理器。iPhone 5s 及以上。
  • arm64e:用于 A12 芯片组。iPhone XS/XS Max/XR
  • i386:用于 32 位模拟器
  • x86_64:用于 64 位模拟器

所以,如果你在模拟器上使用框架,框架需要支持i386或者x86_64;如果你在 iPhone 6 上运行你的应用程序,框架需要支持 arm64 架构。

因此,在大多数情况下,一个框架需要支持所有上述架构。

现在回到如何解决问题。我们需要为设备和模拟器构建框架。

如何为设备构建:

  1. 在“常规”中,指定“部署目标”。
  2. 在“构建设置”中,将“仅构建活动架构”设置为“否”
  3. 在“构建设置”中,确保“有效架构”列出了您需要的所有架构。通常我们只使用默认选项(arm64、arm64e、armv7、armv7s)。
  4. 转到“编辑方案”->“运行”->“构建配置”,将“构建配置”更改为“发布”
  5. 选择活动方案“通用 iOS 设备”。点击 Cmd+B 构建项目。
  6. 右键单击 Xcode 项目中“Products”文件夹下的 [MyFrameworkProject].framework,然后点击“Show in Finder”。[MyFrameworkProject].framework 支持步骤 2 中指定的所有架构。
  7. 将 [MyFrameworkProject].framework 拖到需要使用该框架的项目中。此外,将 [MyFrameworkProject].framework 也拖到“Embedded Binaries”。

如何为模拟器构建:

  1. 步骤 1 至 4 同上。
  2. 选择任何模拟器作为活动方案。点击 Cmd+B 构建项目。
  3. 步骤 6 至 7 同上。

如何为设备和模拟器构建:

有了设备框架后,您需要将模拟器框架和设备框架与“lipo”命令组合在一起。将模拟器的框架重命名为 [MyFrameworkProject]_sim.framework 并将两个框架复制到同一个文件夹中。在终端中运行以下命令(确保您在文件夹中):

lipo -create -output [MyFrameworkProject].framework/[MyFrameworkProject] [MyFrameworkProject].framework/MyFrameworkProject [MyFrameworkProject]_sim.framework/MyFrameworkProject

现在 [MyFrameworkProject].framework 是支持模拟器和设备的最终产品。

于 2019-01-08T03:22:44.387 回答
0

对不起,我让这个话题开放了这么长时间......你们中的一些人已经正确回答了我的问题。当我写这个问题时,我没有发现一个完整(也称为胖)框架(准备使用)中有两个切片。一个用于 iOS 设备,一个用于模拟器。当我发现我使用 lipo 命令制作了一个脚本来融合两个切片并自动得到一个完整的框架时。

于 2018-03-05T16:54:11.340 回答