首页 文章

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

提问于
浏览
49

我正在尝试为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架构(包括模拟器)的框架 .

10 回答

  • 2

    根据所有回复,post on raywenderlich.comChris Conway创建的gist我想出了this .

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

    • 在框架的项目中创建一个新的(聚合)目标

    • 在"Build Phases"下选择"Add Run Script"并复制this file的内容

    • Select Scheme 选择下拉列表中的 Aggregate target

    • Build targetaggregate scheme

    希望能帮助到你 :)

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

    UPDATE 在Xcode 7和8中,单击文件>新建>目标...然后选择"Other"组以选择聚合目标

  • 13

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

    工作区:

    • 创建工作区 .

    框架项目:

    • 在Workspace中创建一个iOS Cocoa touch Framework 项目 .

    • 添加一个简单的Objective C类 MyClass (头.h和实现文件.m),并创建一个方法 - (void)问候语 .

    • 转到项目 Build Phases > Headers >将MyClass.h从 Project 移动到 Public .

    • 将方案更改为框架项目并选择iOS模拟器,然后构建 . (如果集成此框架的应用程序在设备上运行,请选择iOS设备 . 我将继续在此示例中使用模拟器)

    • 您应该没有问题构建,框架构建可以在 Derived Data 目录中找到,您可以在 Organizer 中找到该目录 .

    应用项目:

    • 在Workspace中创建Swift Single View应用程序 .

    • 将iOS模拟器框架构建(在Debug-iphonesimulator或Release-iphonesimulator中找到)拖到项目中 .

    • 创建一个桥接头来向Swift公开Objective C类方法 .

    • 在桥接头文件中导入MyClass.h .

    • 请注意,如果找不到MyClass定义,则添加构建设置 Header Search Paths 的框架 Headers 路径 .

    • 在ViewController.swift的viewDidLoad中创建 MyClass 的实例,然后调用问候语 .

    • 将框架添加到Target> Embedded Binaries

    • 将方案更改为应用项目并选择 iOS simulator ,然后构建 .

    • 您应该能够看到问候消息 .

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

    • 为模拟器构建库

    • 为设备构建库

    • 使用 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是我的框架项目名称 .

  • 1

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

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

    • 为模拟器构建库

    • 为设备构建库

    • 在您的框架的控制台 Products 文件夹中打开(您可以通过打开框架文件夹和 cd .. 从那里打开它)

    enter image description here

    enter image description here

    • this script 来自 Products 文件夹 . 它在此文件夹中创建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"
    

    要么:

    • 通过此脚本使用 lipo 组合这两个框架(将 YourFrameworkName 替换为您的框架名称)
    lipo -create -output "YourFrameworkName" "Debug-iphonesimulator/YourFrameworkName.framework/YourFrameworkName" "Debug-iphoneos/YourFrameworkName.framework/YourFrameworkName"
    
    • 用新的二进制替换现有框架之一:
    cp -R Debug-iphoneos/YourFrameworkName.framework ./YourFrameworkName.framework
    mv YourFrameworkName ./YourFrameworkName.framework/YourFrameworkName
    

    • 利润: ./YourFrameworkName.framework - is ready-to-use fat binary! 您可以将其导入项目!

    For project, that not in xcworkspace-es:

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

  • 47

    我这样做的方式类似于vladof,但希望有点简单 . 我将框架作为app项目的子项目 .

    框架项目

    • 创建一个新的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)

    • 关闭项目

    应用项目

    • 创建一个新的单视图应用程序项目,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中,添加导入MyLib

    • 在viewDidLoad中,添加以下行:

    • var x:MyLib = MyLib()

    • println("(x.greetings())") -

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

  • 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}"
    
  • 0

    自上面的链接之前我做的一个旧视频不再可用了 .

    How to Create a Cocoa Touch Framework Using Xcode 6

  • 1

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

    • 相关链接中的脚本需要修改才能复制iphonesimulator和iphoneos中的所有文件,因为swift具有arm和i386的独立编译文件

    • 确保在iphonesimulator的构建中有ARCHS = "x86_64" ONLY_ACTIVE_ARCH = NO,以避免模拟器出现链接器问题

    • 确保您的接口/类扩展了NSObject,否则当您尝试在swift中使用代码时会遇到问题(它会抱怨无法使用()创建对象 .

  • 1

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

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

    • 在Framework项目中创建一个新的(聚合)目标

    • 在工作区中选择聚合并添加"New Run Script Phase"

    • 将以下代码放入其中:

    #!/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}"
    
    • 在“方案选择”下拉列表中选择“聚合”

    • Build,你做完了!

    问题是模拟器目录指向一个不存在的目录,在2个地方将“Framework”更改为“$ ”诀窍:)

  • 64

    想在skywinder here提供的脂肪脚本答案中添加一些东西 . 我按照步骤但仍然无法让我的框架在模拟器上运行,只有设备 . 解决这个问题:

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

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

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

  • 10

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

相关问题