Tag: 优化

减少快速的应用程序构建时间和代码优化。

在处理基于大型产品的应用程序时,我发现应用程序需要花费更多时间来构建应用程序。 因此,我的下一个目标是减少构建以及代码优化。 就我而言,最初的目的是使用Build Time Analyzer for Xcode来识别并修复最耗时的区域。 如何为Xcode设置构建时间分析器。 转到此链接 ,下载或克隆项目。 之后,您只需要将该项目打开到xcode中,然后按CMD + B即可构建该项目。 构建完成后,您需要存档构建。 归档完成后,您需要导出此构建,并选择选项“ 导出为macOS App” 。 现在将其保存在您的位置并打开该文件夹并安装应用程序。 那个 现在最大的问题是此应用程序如何识别我们的应用程序并生成分析报告。 遵循此指示 我们在应用程序上添加自定义标志。 1.选择目标,然后转到Build Settings-> Swift Compiler-> Other Swift Flags并添加此代码-Xfrontend -debug-time-function-bodies。 确保将以下标志添加到目标的“构建设置”。 2.清理项目(CMD + Shift + K) 3.构建您的项目(CMD + B),然后等待它完成。 构建完成后,转到“ 构建时间分析器应用程序” ,然后单击您的应用程序。 现在,您可以查看Build Time Analyzer App生成的报告,并分析哪些功能和代码需要花费更多时间。 基于此,您可以优化代码并减少构建时间。 确保您遵循的是正确的快速语法格式。 另外,您需要使用耗时较少的功能。 例:- 使用if-else语句的保护瞬间。 谢谢 希望您今天学到了新东西。 请享用!! […]

一名开发人员进行A / B测试

应用内购买的A / B测试 在过去的几个月中,我被问到许多有关A / B测试的问题。 这些问题来自不同的角度。 它们通常是这样的: 你做过A / B测试吗? A / B测试复杂吗? 您认为A / B测试值得吗? 即使我在A / B测试方面有一定经验,但我仍感觉自己是从不属于我的数据中发言。 因此,我想我将使用自己的一个应用程序创建一个小的公共实验。 这些就是结果。 设置 我有一个名为1RepMax的应用程序。 这是一个举重应用程序,当您执行最大举重程序的百分比时使用。 该应用程序自2010年以来一直存在,并且非常受欢迎。 它在该类别中有一些竞争,但我相信它是用于此目的的顶级应用程序之一。 在过去的一年中,我添加了三项消耗性应用程序内购买,以便用户通过给我小费来欣赏我多年来所做的工作。 我收到了一些提示,但没有什么意义。 因此,我想我应该考虑使用A / B测试来查看它是否对我的数字有所改善。 目前,应用内购买如下所示: 新面貌 在过去的一年中,Overcast播客应用程序的所有者Marco Arment改变了他的商业模式,从应用程序内购买功能解锁转变为顾客模型(Marco现在正在尝试广告,我完全支持,但是那是一篇帖子另一时间)。 赞助人模型很像我在应用程序中所做的事情,但是我称它为小费。 很快让我震惊的是,打电话给这个顾客的支持是一种更好的讨钱方式。 所以我想我可能会改用这种语言。 为什么不将其转换为A / B测试? 经过一番快速研究,我决定跟随Marco的领导,按照他目前的三个方案进行布局。 使用赞助人的术语和三个月的等级来描述用户的期望。 新屏幕如下所示: 在将应用提交到Appstore之前,我要做的另一件事是,将用于启动应用内购买屏幕的加号图标更改为应用程序图标本身的变体。 我这样做是为了吸引该应用程序的长期用户的关注。 就像我提到的那样,此应用程序自2010年以来一直存在,因此我认为进行此更改可能很有意义。 我正在使用我最熟悉的A / B测试工具Optimizely作为此实验的测试工具。 结果 最初发行时,我已将该测试作为A和B段之间的50/50分割加载。 我发现应用内购买数量增加了8%。 三层之间的分布与以前大致相同。 […]

加快Xcode行为

Xcode行为可以改变Xcode响应某些事件的方式。 默认行为可以通过在遇到断点时显示例如调试导航器来帮助您,但是它们可以使您的工作速度大大提高。 默认情况下未启用的行为列表。 起点是“行为”选项卡,可以在Xcode首选项中找到该选项卡,可以通过cmd + ,或使用Xcode -> Preferences菜单将其打开。 令人惊讶的是,这是事实。 当构建失败时,默认情况下,问题导航器不会打开。 这样的结果是您自己导航到问题导航器,以查看出了什么问题。 通过导航到当前日志,您甚至可以快速过滤错误,仅查看错误所在。 当测试失败时,您想导航到该测试并在修复后再次运行。 导航到失败的测试的起点是“测试导航器”。 您正在编写代码并运行应用程序以测试功能。 出现断点或问题,您突然进入了一个不同的文件,而这并不是您一直在等待的文件。 通过在运行的应用程序暂停时打开一个新标签页,您只需关闭调试标签页即可完成调试会话并返回工作文件。 您可以在应用程序暂停测试时执行完全相同的操作。 Xcode提供了更多功能来加快您的工作流程。 您可以阅读工作流类别中的更多文章,或查看我的有关加速作为iOS开发人员的演讲。 最初发表于 SwiftLee 。 更多帖子和更新: @twannl

将CocoaPods与预先构建的框架一起使用而不是源文件

介绍cocoapods-binary cocoapods-binary是CocoaPods的插件,旨在将预构建的框架链接到项目。 一旦执行pod install命令,它将自动为每个依赖项预先构建一个框架,并将结果链接到您的项目。 这样就可以减少项目的构建时间 ,因为您将仅构建源代码,而不是每次都构建依赖关系。 这确实是朝正确方向迈出的一步。 安装cocoapods-binary 为了安装cocoapods-binary ,我们需要运行brew install cocoapods-binary或gem install cocoapods-binary命令。 在我们的Podfile中使用cocoapods-binary 我们需要在Podfile的开头添加以下行: plugin ‘cocoapods-binary’ 如果我们要对所有依赖项使用预构建的框架,则还可以添加all_binary! 在任何目标或定义之前。 如果我们再次运行pod install ,它将为每个依赖项预先构建一个框架,并将其与我们的项目链接。 如果我们清理并构建它,它将不再浪费大量时间来构建依赖项。 将比特码嵌入到预先构建的框架中 如果我们想将Bitcode嵌入到我们的iOS应用程序中,我们还需要将其嵌入到我们的依赖项中 。 为了使其与预先构建的框架一起使用,我们需要在Podfile中通过添加enable_bitcode_for_prebuilt_frameworks!进行指定enable_bitcode_for_prebuilt_frameworks! 和keep_source_code_for_prebuilt_frameworks! 毕竟all_binary! 。 请注意,您将需要重新运行pod install才能看到更改。 添加例外 如果我们尝试使用预先构建的框架(例如Google Maps iOS SDK)添加某些依赖项,则这些依赖项将不起作用 。 由于它要求将GoogleMaps.bundle文件添加到主项目并在构建时进行复制,因此不建议使用预构建的框架。 为了使它们正常工作,我们需要通过将:binary => false设置为我们要排除的每个依赖项来添加一个例外 。 pod ‘GoogleMaps’, :binary => false 让我们看一个例子 考虑到我们有以下Podfile: 插件’cocoapods-binary’ 平台:ios,“ 12.0” use_frameworks! […]

让我们为Xcode开发人员优化使用终端的工作。

每个iOS开发人员通常都必须通过Terminal处理需要完成的事情: 使用git ; 通过Fastlane为客户上传版本; 通过Carthage,Cocoapods或SPM等更新依赖项。 最近,我经常不得不在不同的Xcode项目之间切换,并且每次我必须在终端中更改到特定项目的路径时,都不得不切换。 我很累了! 我想有机会使用已指定的路径直接从Xcode打开终端。 🤔如果您有同样的感觉,请继续阅读! 首先 ,我们需要创建一个具有以下主体的脚本: 为此行为选择一个可识别的名称,设置快捷方式,选择“ Run复选框,然后选择先前配置的脚本。 您只需要配置一次即可,它适用于所有项目。 🎉 谢谢阅读! 希望您喜欢我的终端机优化技巧。 如果您不得不面对相同的问题,但以不同的方式解决了该问题,请在下面的评论部分中分享您的解决方案。 🙏 蓬松的iOS工程师。 被动式开放源代码爱好者和保留周期检测器🙂 还要检查我有关编程,Twitter和开源项目的电报频道:

Swift中函数的通用专业化

在今天的帖子中,我们将讨论Swift编译器的Generic专业化知识,并对它是什么以及SIL Optimizer为什么执行此操作以获得更高性能的代码进行基本概述。 此外,我们还将简要介绍Swift中间语言(SIL)表示形式以及它在此通用专业优化遍历中的作用。 首先… 编译器 在深入研究泛型专长之前,我们需要至少从高层次上了解有关编译器流程的一些知识。 swift编译器获取您的Swift代码,将其处理为手动编码的Lexer,将其标记化并将其转换为抽象语法树(AST),然后是语义分析(Sema),其中编译器将使用解析器生成的AST并进行类型检查的AST并检查其中的语义问题。 然后,Swift中级语言生成(SILGen)阶段将通过语义分析生成的AST转换为他们所谓的原始SIL,在对SIL进行了一些优化(如通用专业化,ARC优化等)之后……它生成了这种被称为规范SIL的优化SIL。然后将其交给IRGen生成中间表示(IR),该中间表示将传递给LLVM,以使其继续工作并生成目标文件(.o),该文件随后将由链接器粘合在一起并生成最终的二进制文件。 这是对编译器管道的简要概述。 这只是让我们了解编译器的各个阶段,但是在这里,我们将重点介绍发生通用专业化的SILGen和SIL Optimization Pass。 仅供参考,Swift中间语言(SIL)是一种静态单分配形式(SSA形式)中间表示形式,用于实现swift编译器,目的是在源代码与LLVM IR之间架起抽象桥梁,从而提供一种执行高级别的优化,例如通用专业化,保留/发布优化,动态方法去虚拟化等等……以及高级诊断分析。 您可以在快速主存储库的说明文件[1]中找到有关SIL的更多信息。 返回通用专业化 因此,泛型专业化是SIL优化过程,它分析了对泛型函数的所有调用(使用完整模块编译模式时在模块中)或具有@_specialize属性的泛型代码(我们将在后面讨论),然后生成新的特殊化这些函数的版本将泛型的所有专用用法替换为直接调用生成的专用函数。 使用专用函数的性能更高,因为它可以使用特定的具体类型最有效的布局,并且在诸如将参数复制到局部变量之类的操作中,例如,它避免了动态分配泛型类型和保存对运行时类型元数据大小和对齐方式的值见证表。 让我们看一下如何生成专用SIL的实际示例 生成的SIL上还有很多其他内容,但仅突出显示与我们定义的函数相关的内容。 仅查看生成的SIL,我们就可以看到编译器如何发出通用和专用SIL表示形式。 我们可以注意到,主SIL调用了函数@ $ S4main9incrementyxxs13BinaryIntegerRzlFSi_Tg5的引用,正如我们看到的那样,它是增量的专用版本。 这意味着主要功能正在调用优化的专用功能。 另外,我们可以注意到专用版本针对Int进行了完全优化,而在通用SIL上,所有操作都基于基于元类型信息的调用(应用)函数。 告诉SIL优化器不执行专业化 我们可以告诉编译器,由于某种原因,我们不希望使用@_semantics属性对该通用函数进行专用化。 您可以在HighLevelSILOptimizations文档的“优化语义”属性部分中找到有关语义的更多信息。 这就是本文的全部内容\ o / 如果您有任何意见或疑问,请告诉我。 您的反馈意见非常重要,因此我们可以改善此问题以及将来的帖子,很高兴收到它:)) 您可以在Twitter上@ LucianoPassos11找到我。 感谢您阅读🙂 参考文献 Swift中级Languange(SIL)。 https://github.com/apple/swift/blob/master/docs/SIL.rst SIL程序员手册。 https://github.com/apple/swift/blob/master/docs/SILProgrammersManual.md 泛型。 https://github.com/apple/swift/blob/master/docs/Generics.rst SIL中的高级优化。 https://github.com/apple/swift/blob/master/docs/HighLevelSILOptimizations.rst

如何通过运行时检查来加强代码?

在某些发生运行时类型检查的情况下,无法进行编译时类型检查。 就像从网络上读取数据(当时源代码不知道数据)一样,从用户那里获取输入不满足要求以及由于算术运算等导致的溢出问题。 Swift提供断言来意识到这些问题。 在深入研究Assertions之前,让我们看一下新引入的Swift编译模式和Swift优化级别。 调试构建:增量-Swift支持增量构建目标,即,在更改单个文件时,不重建目标中的每个Swift源文件。 依赖于已修改文件的每个文件都将被重建。 发布版本:整个模块-此选项一起优化目标中整个模块的所有文件,并提高性能。 它可以执行函数内联和函数专业化等优化。 Swift提供了四种不同的优化级别: -Onone :基本上用于常规开发,即调试版本。 它执行最少的优化,并保留所有调试信息。 建议始终在此模式下使用增量编译。 -O :这用于生产代码。 编译器执行了激进的优化,可以极大地改变发出代码的类型和数量。 调试信息将被发出,但是会造成损失。 -Ounchecked :这是一种特殊的优化模式,适用于愿意为性能而牺牲安全性的特定库或应用程序。 编译器将删除所有溢出检查以及一些隐式类型检查。 通常不打算使用此方法,因为它可能导致未检测到的内存安全问题和整数溢出。 仅当您仔细检查了代码对于整数溢出和类型强制转换是安全的后,才应使用它。 -Osize :这是一种特殊的优化模式,在该模式下,编译器将代码大小优先于性能。 此模式适用于整个模块以及单文件编译,而整个模块模式可提供最佳的优化结果。 注意:可以在项目的构建设置中设置优化级别和编译模式。 每当我们需要根据预期的条件检查代码时,就可以使用断言,并且将引发异常。 标准Swift库带有五个断言函数: 断言(_:_:file:line 🙂 assertionFailure(_:file:line 🙂 前提条件(_:_:file:line 🙂 preconditionFailure(_:file:line 🙂 fatalError(_:file:line 🙂 让我们详细研究每个功能: assert():此方法类似于传统的C样式断言,带有可选消息。 它仅应用于在测试过程中处于活动状态但不会影响运输代码性能的内部完整性检查。 如果condition评估为false ,则在打印message后以可调试状态停止程序执行。 示例:对于要入学的孩子,最低年龄要求为3岁,因此我们可以在此处检查此情况: 注意:“ **”优化器可能会假定从未调用此函数。 https://swift.org/blog/whole-module-optimizations/ https://swift.org/blog/osize/ https://developer.apple.com/documentation/swift/swift_standard_library/debugging_and_reflection 采用断言是一个好习惯。 您应该注意使用正确的功能进行适当的构建。 一个小错误可能会导致生产应用程序崩溃,从而影响最终用户。 感谢您的阅读,我们期待您的反馈。 您可以在以下位置找到我: Linkedin个人资料: […]

使iOS应用程序的搜索操作简单高效

在本文中,我想分享如何将搜索功能添加到列出大量数据的ViewController中。 对于iOS应用程序,我们正在开发许多页面以显示数据列表,并且我们添加了搜索功能以使查找所需内容变得容易。 为了简化这些步骤,我在一个经理类中合并了一些功能和操作。 filter :可以随时调用以启动过滤器操作的主要功能。 (例如:用户输入关键字进行搜索时) searchCompleted:完成处理程序,在过滤器操作完成时调用,并返回过滤后的列表。 首先,您的数据模型必须支持可搜索协议才能使其可搜索。 //项应符合可过滤性的协议。 协议可搜索{ func getSearchText()->字符串 } 使用此委托函数,您应该返回将在其中搜索给定关键字的字段。 例如,如果您的模型包含要搜索的name , cuntry和email字段,则委托函数应如下所示: //栏位 var名称:字符串 var country:字符串 var email:字符串 //委托函数 功能 getSearchText ()->字符串{ 返回 姓名 +“ @” + 国家 +“ @” + 电子邮件 } 你应该 使用 BlockOperation 和 OperationQueue 创建一个BlockOperation实例,并将主过滤器操作添加到执行块中。 var filterOperation = BlockOperation ()filterOperation。 addExecutionBlock ({// 筛选操作 … } self.filterOperation。 […]

如何在iOS中快速流畅地进行表格滚动

表格视图是否以60 fps(每秒帧数)滚动? 还是看起来生涩而不光滑? 我的现在以60 fps的速度平稳运行。 它曾经不稳定,平均速度约为38 fps。 这就是我做的。 每行中具有复杂单元格的表可能难以平滑滚动。 您需要在滚动时配置和布局单元,这是有限的时间。 当需要看时,滚动会变得缓慢而生涩。 我的平均速度约为38 fps(尽管分布范围很广)。 显然很慢。 问题在于表单元格是一个自定义视图,其中包含其他自定义视图,并且这些视图的位置和大小受到各种限制。 看起来非常不错,这是一个很棒的UI。 但是,当我们的表视图控制器代表被要求提供行高时,很难很快地弄清这一点。 问题 在我们的应用程序中,表格单元格的大小可能会因数据而异,几乎相差2倍! 因此,我们在调用heightForRowAtIndexPath方法时配置并布局一次单元,然后在调用cellForRowAtIndexPath方法时再次进行所有操作。 我们本可以使用estimatedHeightForRowAtIndexPath方法。 但是对我们来说,如果不进行完整的布局很难估算。 另一位开发人员希望表格准确无误。 因此,他们只是设置单元格视图并在其上进行布局,然后询问该单元格视图的高度。 那行得通,而且是准确的。 但是非常慢。 为什么我要关心的是38 fps,而不是60? 应用程序的响应性在快速动作的工作方式(如滚动等快速动作)中非常明显。 如果滚动是平滑的,则用户可以快速流畅地查看应用程序。 而且以平均38 fps的速度不规律地滚动时,会显得生涩且不流畅。 60 fps的速度与屏幕更新的速度一样快,因此必须每16毫秒更新一次(实际上由于系统和图形开销而要少)。 我在委托中的heightForRowAtIndexPath和cellForRowAtIndexPath方法上设置了一个断点,并看到多次调用该断点。 这段代码每次都在重做单元格布局。 有趣的是,我看到在heightForRowAtIndexPath.之前叫做cellForRowAtIndexPath heightForRowAtIndexPath. 与我预期的相反,但这有所帮助。 第一次尝试 我试图简化该过程,并根据行的元素和潜在的配置来计算高度。 这演变成一系列让我不安的特殊情况。 这很复杂,无法支持。 这意味着更改单元格不仅仅是更改xib文件的问题,而是再次对布局计算重新进行反向工程以估计或计算高度。 我不喜欢为要维护代码的人添加地雷。 (可能是我!)这行不通。 真正的解决方案: 因此,我回到了“布局并询问高度”方法。 但是这一次,我将在单元格上进行布局时缓存高度。 我创建了一个像这样的类: @interface MyCachedRowHeight : NSObject @property […]