ReactNative进阶(三十四):ipa Archive 阶段报错error: Multiple commands produce问题修复及思考

文章目录

    • 一、前言
    • 二、问题描述
    • 三、问题解决
    • 四、拓展阅读
    • 五、拓展阅读

一、前言

在应用RN开发跨平台APP阶段,从git中拉取项目,应用Jenkins进行组包时,发现最终生成的ipa安装包版本号始终与项目中设置的版本号不一致。

二、问题描述

经过仔细排查,发现Jenkins在Archive编译、归档阶段失败,但是后续Export阶段生成了ipa包。

error: Multiple commands produce '/Users/xxx/Library/Developer/Xcode/DerivedData/xxx-eomylkmdzkgaughihoblturddotc/Build/Products/Debug-iphonesimulator/PopNews.app':1) Target 'xxx' has create directory command with output '/Users/xxx/Library/Developer/Xcode/DerivedData/xxx-eomylkmdzkgaughihoblturddotc/Build/Products/Debug-iphonesimulator/PopNews.app'2) That command depends on command in Target 'xxx': script phase “[CP] Copy Pods Resources”

三、问题解决

选中项目 target -> Build phase -> Copy Pods Resources -> Output Files -> 移除 ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH} -*

四、拓展阅读

androidandroid/app/build.gradle文件中版本设置如下:

android {compileSdkVersion rootProject.ext.compileSdkVersioncompileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}defaultConfig {applicationId "com.china.shq5785"minSdkVersion rootProject.ext.minSdkVersiontargetSdkVersion rootProject.ext.targetSdkVersionversionCode 18072801versionName "2.2.5"multiDexEnabled truetestBuildType System.getProperty('testBuildType', 'debug')// This will later be used to control the test apk build typetestInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'ndk {//设置支持的SO库架构abiFilters "armeabi", "armeabi-v7a", "x86_64" //, "arm64-v8a"}missingDimensionStrategy 'react-native-camera', 'general'}......
}

ios 在配置文件ios/mrcs.xcodeproj/project.pbxproj中,可查看到如下配置信息:

13B07F941A680F5B00A75B9A /* Debug */ = {isa = XCBuildConfiguration;baseConfigurationReference = AA6AA411A14368FB4EEC0CD3 /* Pods-mrcs.debug.xcconfig */;buildSettings = {ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;CLANG_ENABLE_MODULES = YES;CODE_SIGN_IDENTITY = "iPhone Distribution";CODE_SIGN_STYLE = Manual;CURRENT_PROJECT_VERSION = 1;DEAD_CODE_STRIPPING = NO;DEVELOPMENT_TEAM = U4ALRF5A38;ENABLE_BITCODE = NO;FRAMEWORK_SEARCH_PATHS = ("$(inherited)","$(PROJECT_DIR)/shq5785","$(PROJECT_DIR)",);GCC_PREFIX_HEADER = shq5785/PrefixHeader.pch;GCC_WARN_ABOUT_RETURN_TYPE = NO;HEADER_SEARCH_PATHS = "$(inherited)";INFOPLIST_FILE = shq5785/Info.plist;IPHONEOS_DEPLOYMENT_TARGET = 9.0;LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";LIBRARY_SEARCH_PATHS = ("$(inherited)","$(PROJECT_DIR)/shq5785",);MARKETING_VERSION = 2.2.5;OTHER_CODE_SIGN_FLAGS = "--deep";OTHER_LDFLAGS = ("$(inherited)","-ObjC","-lc++",);PRODUCT_BUNDLE_IDENTIFIER = com.china.shq5785;PRODUCT_NAME = shq5785;PROVISIONING_PROFILE_SPECIFIER = "1111";SWIFT_OBJC_BRIDGING_HEADER = "$(PRODUCT_MODULE_NAME)/shq5785-Bridging-Header.h";SWIFT_OPTIMIZATION_LEVEL = "-Onone";SWIFT_VERSION = 5.0;VERSIONING_SYSTEM = "apple-generic";};name = Debug;};

五、拓展阅读

iOS 开发的最后一步就是进行 App 的打包和分发,这里分为两个步骤:

  1. Archive:对 Target 进行编译、归档,生成 .xcarchive 文件。

  2. Export:对 .xcarchive 归档文件进一步处理,生成不同渠道的 .ipa 包,进行分发。

当我们在 Xcode 菜单中选择 Product -> Archive 后,编译系统就会对当前的 Xcode 工程进行分析、编译和打包,最终生成目标 Target 的一个 Archive(归档),我们可以在 Window -> Organizer -> Archives 页面查看到所有缓存的历史归档信息:

在这里插入图片描述
所谓的”归档“,就是对源码进行编译后,将此次编译生成的各种文件、资源、记录统一封装到一个地方,方便进行管理和回溯。

右键选择一个归档文件 archive,然后点击 Show in Finder,可以看到它在 Finder 中表示为一个 .xcarchive 后缀的文件。

在这里插入图片描述

这个 .xcarchive 文件包含了应用和它的符号表信息(symbol information)以及其它的相关资源,右键选择显示包内容,可以查看一个 Archive 归档中具体的文件结构:

在这里插入图片描述
其中每个文件夹的含义:

  • BCSymbolMaps
    Xcode 对 BitCode 符号表进行混淆(Symbol Hiding)后生成的对照表,和 dSYM 文件会一一对应。

  • dSYMs
    存储此次编译的符号表(debug symbols),用来符号化解析崩溃堆栈。

  • Products
    存储此次编译生成的的 App 包(.app)。

要注意的是这个包虽然包括了 App 运行需要的可执行文件以及其它资源,但是和最终用户下载的版本会有所不同。后续的 export 操作会对其进行进一步处理。

  • SCMBlueprint
    如果 Xcode 打开了版本管理(Preferences -> Source Control -> Enable Source Control),SCMBlueprint 文件夹会存储此次编译的版本控制信息,包括使用的 git 版本、仓库、分支等。

如果未来想要回溯此次编译的源码版本,可以从这个 SCMBlueprint 中找到必要的信息。

  • SwiftSupport
    如果在 Target 的 Build Settings 中打开了ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES,此次编译使用的 Swift 版本对应的标准库文件(.dylib)会被放到这个文件夹中。

发布 App 时,这些标准库也会被复制到 ipa bundle 中。

不过现在 Swift 的 ABI 已经稳定了,Xcode 10.2 及以后的版本打出来的包,在 iOS 12.2 及以后的系统的 app bundle 中不用再自带链接库了,节省了一定的体积。

了解 ipa 文件
.ipa(iOS App Store Package) 文件是最终被安装到 iPhone 上的应用格式,包含了运行 App 所必需的的签名、二进制包、资源等内容。

在 Organizer 中无论用什么方式 export 应用的安装包,最终生成的都是一个 .ipa 文件。

.ipa 本身是压缩包文件,如果要查看 ipa 中的内容,可以右键查看包内容,观察解压以后的包,主要包含以下内容:

在这里插入图片描述
App 的签名信息会被放到 _CodeSignature 文件夹中。

  • info.plist
    存储 App 主要信息的 plist 文件也会被一并打包到 ipa 中。

  • entitlements
    entitlement 直译成中文是“权益”、“权限”的意思。

当你在 Capabilities 中开启一些特定的权限时,Xcode 会自动给你生成一个.entitlements 文件,在这个文件中通过 xml 的格式将这些授权记录下来。

App 瘦身
要对 App 安装包体积进行压缩,首先要知道安装包占用的多少空间,这些空间由哪些部分组成,然后再进行针对性的优化。

查看最终用户安装包大小
实际上在 Xcode 本地 archive 出来的 app 包或者 export 出来的 ipa 包和最终用户下载的版本会有所不同(通常体积会大很多)。因为苹果可能会对 App 进行重新编译(如果上传了 BitCode),也会针对不同的设备型号、iOS 版本分发不同的资源(比如 2x、3x 的图片),最后还会对整个 .ipa 进行压缩,以减少从 App Store 下载时耗费的流量。

那么如何估算用户最终下载版本的包体积大小呢?其实在 iTunes Connect 页面可以直接查询到。

打开 iTunes Connect,选择 我的App -> 活动 -> 所有构建版本,然后选择一个要查看的版本:

在这里插入图片描述
找到 App Store 文件大小按钮:

在这里插入图片描述

在弹出的列表中,可以看到在最新版本的 iOS 系统下,不同设备下载的包体积大小:

在这里插入图片描述

列表中的两列:

  • 下载大小:表示通过无线下载的压缩 App 大小;

  • 安装大小:安装后此 App 将在用户设备上占用的磁盘空间大小;

如何分析 App 包 Size?
为了更直观地查看哪些资源占用了 App 安装包的体积,我们可以借助一些文件工具来分析解压后的 ipa 包,比如说 derlien

在这里插入图片描述

可以很直观地看到各种不同类型文件所占的比例。

检查未使用资源
随着 App 的不断迭代,我们往往会无意间引入很多用不到的资源,或者一些资源的引用已经从代码中去除了,但是没有及时从 bundle 中删除,造成 App 包体积的浪费。

为了查找这些不再使用的资源,可以借助开源工具 LSUnusedResources 来检测整个工程。

LSUnusedResources 应用过程如下:

  1. 可以从下面的地址下载 LSUnusedResources
    源码,然后进行编译…

  2. 将源码在Mac上运行,可以看到如下界面:在这里插入图片描述在Project> Path目录中,点击Browse…选择要检测工程的根目录,然后点击Search,开始进行检索…,你可以在下方的日志窗中看到检测结果>

  3. 检测完成后,可以点击Export将此日志导出,然后开始进行清理工作.切勿不管三七二十一直接开删,毕竟是机器检测,不可完全信赖。

针对一些特殊情况,比如代码中使用例如 [UIImage imageNamed:[NSString stringWithFormat:@"icon_tag_%d", index]] 的方式引用资源,LSUnusedResources 也支持使用正则表达式来模糊匹配。

压缩图片
图片文件是安装包中最常见的资源了,常常会占有相当一部分比例,未压缩的图片体积往往相当大,通过一些工具压缩图片资源,节省空间:

  • 无损压缩:ImageOptim

  • 有损压缩:tinypng

使用 Asset Catalogs 存储资源
相比于直接将图片拖入工程目录的方式,使用 Asset Catalogs 会更节省体积。Asset Catalogs 会用一个高度优化的特殊格式来存所有图片,对 png 图片也会进行最大化的压缩。

Xcode 工程模板会自动生成一个 Assets.xcassets 文件,我们也可以按需创建另外的 .xcassets,最终在 ipa 包中,这些 xcassets 都会被压缩到 Assets.car 文件中,一定程度上也保证了安全性。

640?wx_fmt=png

除了图片资源外,Asset Catalogs 也可以存储文本、Data 甚至 AR、apple TV 相关的资源,非常全能,所以比较好的实践就是:

能用 Asset Catalogs 管理的资源,尽量使用 Asset Catalogs 来管理

分析 LinkMap 文件
上面提到,App 包占用空间中很大一部分比例是最终编译生成的可执行文件(MACH-O),可执行文件的大小不仅和代码体积有关,也受编译器版本、编译选项、链接库、目标架构等影响。

可以通过分析编译时产生的 LinkMap 来了解 MACH-O 文件的组成部分。

要找到对应的 LinkMap,首先在 Xcode Target -> Build Settings -> Write Link Map File 设置为 YES,然后在 Target -> Build Settings -> Path to Link Map File 选项中设置好 LinkMap 的生成地址(一般用 build 文件夹中的默认地址就好了),archive 成功后,我们就可以在对应地址找到该次编译的 LinkMap 了:

640?wx_fmt=png

LinkMap 记录了编译时的链接信息,用来描述可执行文件的构造成分,包括代码段__TEXT 和数据段 __DATA 的分布情况:

640?wx_fmt=png
网上有很多脚本可以对 LinkMap 进行分析统计,比如:

可视化工具

  • js脚本

  • 命令行工具

获取到分析结果后,可以精确了解各个模块、链接库、方法在可执行文件中的位置和占用空间:

640?wx_fmt=png

对于一些占比特别大的模块,常见的优化思路有:

寻找可替代的,小体积的依赖库,或者自己实现

去掉静态库中不需要的指令集,比如 armv7s,x86等,只保留发布需要的 armv7,arm64

提高代码重用性

进一步分析代码中没有被使用的方法、模块,对代码库进行精简。

使用 bitcode
bitcode 是在 LLVM 体系中介于前端语言(OC、Swift、C)和后端语言(X86、ARM的机器码)之间的中间语言。

640?wx_fmt=png

一次完整的编译(从源码到.O目标文件)包含三个主要步骤:

  • 前端(Frontend):负责把各种类型的源代码编译为 bitcode 中间码表示。

  • 优化(Optimizer):负责对 bitcode 进行各种类型的优化,将 bitcode 代码进行一些逻辑等价的转换,使得代码的执行效率更高,体积更小。

  • 后端(Backend):也叫 CodeGenerator,负责把优化后的 bitcode 编译为指定目标架构的机器码,比如 x86、arm64 等等。

可以在 Xcode Target -> Build Settings -> Enable Bitcode 中打开 bitcode 选项,这样在 archive 时,会将中间生成的 bitcode 嵌入到链接后的二进制文件(.o)中,用于提交到 App Store。

上面提到,bitcode 作为 LLVM 的中间语言,是可以从它直接编译出最终程序的,Apple 拿到我们上传的 bitcode 后,会使用最新的技术、编译器针对不同的终端设备重新编译 App,而这些重新编译的版本往往比本地 Xcode 编译的版本体积更小、效率更高。

如果后续需要支持新的平台或者有新的编译技术革新,苹果就不用依赖开发者重新上传了,直接使用现成的 bitcode 编译出新的版本。

值得注意的是:在打包时,如果一些三方的依赖库没有开启 bitcode,或者开启了但是没有在最终引用的链接库中带有 bitcode,那么整个工程就无法用 bitcode 来编译了。

按需加载资源(On-Demand Resources)
iOS9 以后,苹果提供了 On-Demand Resources 功能来减少安装包的体积。可以将一些资源标记为 “按需加载”,在需要使用的时候请求操作系统从 App Store 中下载。这个功能非常适合一些大型游戏、带有付费内容或者大量不常使用的多媒体资源的 App。

在这里插入图片描述

当然,按需加载只是针对 App 使用的资源文件,不包括二进制可执行文件或者源码。

On-Demand Resources 的配置可以很轻松地在 Xcode 中完成。

首先在 Target -> Resource Tags 中创建资源 tag,一个 tag 表示一组可以被独立下载的资源,后面我们就会使用这个 tag 在程序中请求操作系统下载对应的资源包到本地。

640?wx_fmt=png

不同的 tag 包含的资源是可以重复的,App Store 会自己 differ,不会重复下载。

然后找到想要按需加载的资源文件,为它们分配一个或多个之前创建的 tag。

640?wx_fmt=png

最后在代码中,可以使用 NSBundleResourceRequest

  • 请求下载 on-demand 资源;

  • 将资源标记为已使用状态(这样下载的资源会被清理掉,节省本地空间);

  • 管理资源下载过程,配置优先级、追踪下载进度等等;

  • 检测磁盘容量警告;

下面的代码是一个简单的资源下载请求:

// 配置要下载的 tags
NSSet *tags = [NSSet setWithObjects: @"birds", @"bridge", @"city"];// 创建 NSBundleResourceRequest 对象
resourceRequest = [[NSBundleResourceRequest alloc] initWithTags:tags];// 请求资源,处理回调
[resourceRequest beginAccessingResourcesWithCompletionHandler: ^(NSError * __nullable error) {if (error) {// 处理错误self.resourcesLoaded = NO;return;}// 下载成功,可以直接使用这些资源了self.resourcesAvailable = YES;}
];

下图总结了一个 on-demand 资源的生命周期:

640?wx_fmt=png

题外话:苹果取消了移动网络下载 150M 的限制,说明随着手机容量的增加和移动网络的普及,大家对 App 安装包体积不再那么敏感了,只要我们遵循一些最佳实践,一般不会在这一块有太大的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/44323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数学】CF1242 A

Problem - 1242A - Codeforces 题意&#xff1a; 思路&#xff1a; Code&#xff1a; #include <bits/stdc.h>#define int long longusing i64 long long;constexpr int N 2e3 10; constexpr int M 2e3 10; constexpr int mod 998244353; constexpr int Inf 1e1…

Rabbitmq的应用场景

Rabbitmq的应用场景 一、异步处理 场景说明&#xff1a;用户注册后&#xff0c;需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 2.并行的方式 ​​串行方式​​: 将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有…

apex安装出错:TypeError unsupported operand type(s) for +: “NoneType“ and “str“

Windows 10 环境下安装apex报错&#xff1a;TypeError unsupported operand type(s) for : “NoneType“ and “str“ 1、首先apex不能直接pip install apex安装。 2、具体安装步骤&#xff1a;【python】【深度学习】apex的安装_apex python_愿东大没有食堂的博客-CSDN博客 …

GitLab与GitLab Runner安装(RPM与Docker方式),CI/CD初体验

背景 GitLab 是一个强大的版本控制系统和协作平台&#xff0c;记录一下在实际工作中关于 GitLab 的安装使用记录。 一开始使用 GitLab 时&#xff0c;是在 CentOS7 上直接以 rpm 包的方式进行安装&#xff0c;仅作为代码托管工具来使用&#xff0c;版本&#xff1a; 14.10.4 …

类之间的比较

作者简介&#xff1a; zoro-1&#xff0c;目前大一&#xff0c;正在学习Java&#xff0c;数据结构等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 类之间的比较 固定需求式比较器 固定需求式 通过…

FPGA: RS译码仿真过程

FPGA: RS译码仿真过程 在上一篇中记录了在FPGA中利用RS编码IP核完成信道编码的仿真过程&#xff0c;这篇记录利用译码IP核进行RS解码的仿真过程&#xff0c;带有程序和结果。 1. 开始准备 在进行解码的过程时&#xff0c;同时利用上一篇中的MATLAB仿真程序和编码过程&#x…

论文《LoRA: Low-Rank Adaptation of Large Language Models》阅读

论文《LoRA: Low-Rank Adaptation of Large Language Models》阅读 BackgroundIntroducitonProblem StatementMethodology Δ W \Delta W ΔW 的选择 W W W的选择 总结 今天带来的是由微软Edward Hu等人完成并发表在ICLR 2022上的论文《LoRA: Low-Rank Adaptation of Large Lan…

恶意软件分析与反制: 深入研究各类恶意软件,介绍分析技术以及如何构建有效的反恶意软件策略

第一章&#xff1a;引言 在数字化时代&#xff0c;恶意软件已经成为网络安全领域的一大威胁。从病毒、蠕虫到特洛伊木马&#xff0c;各类恶意软件不断进化&#xff0c;威胁着个人、组织 ja以国家的信息安全。本文将深入探讨恶意软件的分析与反制方法&#xff0c;帮助读者更好地…

Log4net在.Net Winform项目中的使用

引言&#xff1a; Log4net是一个流行的日志记录工具&#xff0c;可以帮助开发人员在应用程序中实现高效的日志记录。本文将提供一个详细的分步骤示例&#xff0c;来帮助您在.Net Winform项目中使用Log4net。 目录 一、安装Log4net二、配置Log4net三、在项目中使用Log4net四、初…

Segment Anything论文阅读笔记

Segment Anything论文阅读笔记 1. Segment Anything论文基本信息2. Segment Anything论文阅读2.1 第一遍阅读 Segment Anything2.2. 第二遍阅读 Segment Anything2.2.1. Segment Anything中相关的图表 1. Segment Anything论文基本信息 论文地址https://arxiv.org/abs/2304.02…

基于Python的HTTP代理爬虫开发初探

前言 随着互联网的发展&#xff0c;爬虫技术已经成为了信息采集、数据分析的重要手段。然而在进行爬虫开发的过程中&#xff0c;由于个人或机构的目的不同&#xff0c;也会面临一些访问限制或者防护措施。这时候&#xff0c;使用HTTP代理爬虫可以有效地解决这些问题&#xff0…

Mybatis对参数的处理

环境配置 项目结构 导入依赖 <dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.10</version></dependency><dependency><groupId>mysql</grou…

Mac OS minicom 无法设置921600问题

MacOS minicom 无法设置921600问题 介绍过程解决方案参考资料 介绍 minicom是Mac上一款非常好用的串口工具。本文假设你已经安装minicom&#xff0c;并且知道minicom的一般配置和使用方法。这是“MacOS minicom 无法设置921600”的解决问题记录。它在以下环境中设置成功&#…

后端返回图片资源错误404,前端使用默认图片

后端返回的图片资源可能会因为各种原因&#xff08;后台误删&#xff0c;地址更改未及时更新&#xff0c;损毁&#xff09;出现无法展示的情况&#xff0c;比如这种报错 就会导致图片资源错误&#xff0c;页面出现这种情况 用户体验很不好&#xff0c;为了改善这种情况&#xf…

Webpack node、output.jsonpFunction 配置详解

Webpack node、output.jsonpFunction 配置详解 最近尝试给一些用到 webpack 的项目升级到最新 webpack5 版本&#xff0c;其中遇到了一些问题&#xff0c;我挑了两个比较典型的问题&#xff0c;其中主要涉及到了 webpack 的 node 属性跟 output.jsonpFunction &#xff08;web…

我能“C”——数据的存储

目录 1. 数据类型介绍 1.1 类型的基本归类&#xff1a; 2. 整形在内存中的存储 2.1 原码、反码、补码 2.2 大小端介绍 2.3 练习 3. 浮点型在内存中的存储 3.1 一个例子 3.2 浮点数存储规则 1. 数据类型介绍 char // 字符数据类型 short // 短整…

解决多模块内核心模块有接口打包成jar后被依赖并调用遇到的问题(springcloud集成ruoyi.quartz)

项目准备开发个新功能&#xff0c;刚好很喜欢ruoyi写的任务调度&#xff0c;因此想到了集成ruoyi.quartz模块 &#xff0c;遇到了很多问题: 首先因为ruoyi.quartz模块依赖了ruoyi.common模块&#xff0c;因此第一步我需要把common模块一部分依赖项复制到了quartz模块内&#xf…

Selenium的使用:WEB功能测试

Selenium是ThrougthWorks公司一个强大的开源WEB功能测试工具系列&#xff0c;本系统包括多款软件 Selenium语言简单&#xff0c;用(Command,target,value)三种元素组成一个行为&#xff0c;并且有协助录制脚本工具&#xff0c;但Selenese有一些严格的限制&#xff1a; …

创建和运行 Ansible 临时命令

创建和运行 Ansible 临时命令 作为系统管理员&#xff0c;您需要在受管节点上安装软件。 请按照正文所述&#xff0c;创建一个名为 /home/curtis/ansible/adhoc.sh 的 shell 脚本&#xff0c;该脚本将使用 Ansible 临时命令在各个受管节点上安装 yum 存储库&#xff1a; 存储库…

k8s的pv和pvc创建

//NFS使用PV和PVC 1、配置nfs存储 2、定义PV 实现 下图的pv和pvc测试 pv的定义 这里定义5个PV&#xff0c;并且定义挂载的路径以及访问模式&#xff0c;还有PV划分的大小 vim /pv.yamlapiVersion: v1 kind: PersistentVolume metadata:name: pv001 spec:capacity:storage: …