Xcode 6 iOS创建Cocoa Touch框架 - 架构问题

时间:2014-06-04 13:57:57

标签: cocoa ios8 xcode6 dynamic-library ios-universal-framework

我正在尝试为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应用程序项目做了同样的事情,但没有任何作用。我认为有些事情我不明白。

例如,当我仅为i386(iOS模拟器)构建框架时,请使用命令行进行检查 " xcrun lipo -info MyFramework",我有一个问题

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

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

11 个答案:

答案 0 :(得分:66)

根据所有回复,由post on raywenderlich.com创建的gistChris Conway我提出了this

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

  1. 在框架项目中创建一个新的(聚合)目标
  2. Under" Build Phases"选择"添加运行脚本"并复制this file
  3. 的内容
  4. 方案选择下拉列表中选择 汇总目标
  5. 构建 目标 汇总方案
  6. 希望有所帮助:)

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

    更新在Xcode 7和8中,点击文件>新建>目标...然后选择"其他" group to select Aggregate target

答案 1 :(得分:47)

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

工作区:

  • 创建工作区。

框架项目:

  • 在Workspace中创建iOS Cocoa touch Framework 项目。
  • 添加一个简单的Objective C类 MyClass (标题.h和实现文件.m),并创建一个方法 - (void)greetings
  • 转到项目构建阶段> 标题>将 MyClass.h 项目移至公开
  • 将方案更改为框架项目并选择 iOS模拟器,然后构建。 (如果集成此框架的应用程序在设备上运行,请选择 iOS设备。在此示例中我将继续使用模拟器)
  • 您应该没有问题构建,可以在派生数据目录中找到框架构建,您可以在管理器中找到该目录。

应用项目:

  • 在Workspace中创建一个Swift Single View应用程序。
  • 将iOS模拟器框架构建(在 Debug-iphonesimulator Release-iphonesimulator 中找到)拖到项目中。
  • 创建一个桥接头,将Objective C类方法暴露给Swift。
  • 在桥接头文件中导入MyClass.h。
  • 请注意,如果找不到MyClass定义,则添加构建设置标题搜索路径的框架标题路径。
  • ViewController.swift 的viewDidLoad中创建 MyClass 的实例,然后调用 greetings
  • 向Target添加框架>嵌入式二进制文件
  • 将方案更改为应用项目并选择 iOS模拟器,然后构建。
  • 您应该可以看到问候留言。

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

  • 模拟器构建库
  • 设备构建库
  • 使用 lipo
  • 合并它们

网上有很多关于它的参考资料,例如here

为框架创建通用二进制文件:导航到框架Derived Data目录然后 / 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 是我的框架项目名称。

答案 2 :(得分:13)

以下是创建胖(通用)框架的步骤:

  1. 在项目的ONLY_ACTIVE_ARCH=NO中设置Build Settings

    • 为模拟器构建库
    • 为设备构建库
  2. 在您的框架的控制台Products文件夹中打开(您可以通过开放框架文件夹和cd ..从那里打开它)

  3. enter image description here enter image description here

    1. Products文件夹中运行 this script 。它在此文件夹中创建fat Framework。 (或按照下面 4.5。中的说明手动完成)

      framework_name="${$(basename $(find ./Debug-iphoneos -type d -name '*.framework' -maxdepth 1))%.*}" && \
      cp -R Debug-iphoneos/$framework_name.framework ./$framework_name.framework && \
      lipo -create -output "$framework_name.framework/$framework_name" "Debug-iphonesimulator/$framework_name.framework/$framework_name" "Debug-iphoneos/$framework_name.framework/$framework_name"
      

    2. 或者:

      1. 通过此脚本使用 lipo 组合这两个框架(将YourFrameworkName替换为您的框架名称)

        lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
        
      2. 替换现有框架中的新二进制文件:

        cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
        mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
        

        1. 利润:./YourFrameworkName.framework - 是现成的胖二进制文件!您可以将其导入项目!

        2. 对于项目,不在xcworkspace - es:

          您也可以尝试使用this gist但似乎它不适用于工作空间中的项目。

答案 3 :(得分:10)

我这样做的方式类似于vladof,但希望有点简单。我将框架作为应用程序项目的子项目。

框架项目

  • 创建一个新的iOS Cocoa Touch Framework。称之为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)
  • 关闭项目

App项目

  • 创建一个新的单视图应用程序项目,Swift或Obj-C。称之为MyApp。
  • 从Finder中,将MyLib项目文件拖到MyApp窗口的左侧管理器部分,并确保插入行位于MyApp下方。这使得MyLib成为MyApp的子项目。 (它仍然可以在其他项目中以相同的方式使用)
  • 单击管理器中的MyApp,然后选择MyApp目标并选择Build Phases。
  • 在Target Dependancies中,单击+号并添加MyLib。
  • 在链接库中,单击+号并添加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())") -

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

答案 4 :(得分: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}"

答案 5 :(得分:2)

自上述链接之前我所做的旧视频已不再可用。

How to Create a Cocoa Touch Framework Using Xcode 6

答案 6 :(得分:1)

vladof共享的方法很少有更多适用于基于swift的框架

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

答案 7 :(得分:1)

最后,我让它为我工作! 抱歉黄色的大框架,我不知道如何更好地格式化它。

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

  1. 在框架项目中创建新的(聚合)目标
  2. 在工作区中选择聚合并添加&#34;新运行脚本阶段&#34;
  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. 构建,你已经完成了!
  6. 问题是模拟器目录指向一个不存在的目录,正在改变&#34; Framework&#34;到&#34; $ {PROJECT_NAME}&#34;在2个地方做了诀窍:))

答案 8 :(得分:1)

想在skywinder here提供的lipo脚本答案中添加一些内容。我遵循了这些步骤,但是仍然无法让我的框架在仅设备的模拟器上运行。要解决此问题:

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

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

Voila,她在模拟器和真实设备上工作!这是Xcode 10 / Swift 4.2 btw

答案 9 :(得分:1)

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

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

如果不知道哪种架构代表什么,则不同的iPhone设备具有不同的架构,下面是完整列表:

  • arm6:iPhone 1、2、3G
  • arm7:用于支持iOS 7最早的设备[32位]。 iPhone 4 4秒
  • arm7s:用于iPhone 5和5C [32位]。 iPhone 5、5c
  • arm64:用于iPhone 5S中的64位ARM处理器[64位]。 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. 右键单击Xco​​de项目中“产品”文件夹下的[MyFrameworkProject] .framework,然后单击“在Finder中显示”。[MyFrameworkProject] .framework支持在中指定的所有体系结构 第2步。
  7. 将[MyFrameworkProject] .framework拖到需要使用的项目中 这个框架。另外,也将[MyFrameworkProject] .framework拖到“嵌入式二进制文件”。

如何构建模拟器:

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

如何同时针对设备和模拟器进行构建:

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

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

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

答案 10 :(得分:0)

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