Tag: 软件开发

了解Swift的CharacterSet

tldr:单击 此处 查看 CharacterSet.decimalDigits 所有 CharacterSet.decimalDigits 您是否曾经需要检查过字符串是否仅由数字组成? 标点符号或非字母数字字符的存在如何? 一个人可以使用多种方法,从Formatter类之一到NSScanner甚至是NSPredicate ,但是您发现的最可能的代码片段涉及到使用反向的CharacterSet 。 简而言之, CharacterSet是一个由Objective-C桥接的Swift类,它代表一组Unicode字符。 它的Objective-C对应物NSCharacterSet本身是免费的,与Core Foundation的CFCharacterSet桥接。 CFCharacterSet用C编写,相当古老,至少可以追溯到Mac CFCharacterSet的主要思想是提供一种支持Unicode的数据结构,以帮助有效地搜索Unicode字符串。 NSString和NSScanner内部使用NSCharacterSet进行字符串搜索操作。 可以将CharacterSet初始化为空集,也可以将其初始化为字符串,字节或文件内容中存在的一组字符。 它带有许多方便的预定义集合(例如URL查询片段中允许的字符或字母数字字符),甚至还允许集合代数(例如并集,交集和异或)。 使用CharacterSet的预定义集合之一感觉很方便: 请注意,四个8位和两个16位都加起来为32位。 这完全是设计使然:UTF32是固定宽度格式,UTF8和UTF16可以轻松装入其中,而无需进行任何额外工作。 所有UTF32字符都包含32位,即使不是必需的。 这使格式无效,但是有一个好处 :它非常适合搜索,因为您可以遍历第32位以获得下一个字符,而不用解码每个字节来解码该字符的代码点宽度。 这正是NSCharacterSet.characterIsMember(UTF8 or UTF16 or UTF32)内部调用仅接受UTF32字符的longCharacterIsMember(UTF32)的原因NSCharacterSet.characterIsMember(UTF8 or UTF16 or UTF32) 在CharacterSet搜索字符成员资格的最佳方法是获取该CharacterSet的UTF32代码点,并将其传递给NSCharacterSet的longCharacterIsMember() 。 看起来像这样: 以下是将字符的代码点值转换为UTF8二进制文件的方法:在上表中的所有x s中填充字符的二进制值。 要确定需要多少字节,请考虑二进制字符编码的长度。 1字节的UTF8仅可容纳7位(表中仅7 x s)。 一个2字节的UTF8可以容纳11位。 3字节可容纳16位,而4字节UTF8可容纳21位。 对于“€”字符(U + 20AC 10 0000 1010 1100 ),我们至少需要14位,这意味着它将需要3个字节的结构,可以容纳12至16位。 填充到UTF8结构中的二进制数字如下所示: […]

Cloud Foundry上的蒸气3

您对如何编写服务器端swift代码感兴趣,并且在第一个hello世界之后,您在脑海中听到了声音,例如“在哪里可以运行此代码”? 因此,一种解决方案是在swift运行时内的Cloud Foundry上运行它。 我知道服务器端快速是当前鲜为要求的话题,但是当我开始使用Vapor 3时,我意识到花了一些时间和一些研究才能在Cloud Foundry上运行它。 我的希望是,如果有人在那里尝试相同的内容,将会找到此博客文章,并且有助于使这些内容正常运行。 现在,让我们开始吧,让我解释一下如何将Vapor 3应用程序带到Cloud Foundry。 首先,您需要访问应在其上运行应用程序的Cloud Foundry环境。 接下来,您需要一个快速的应用程序,就我而言,它是一个简单的CRUD服务,可以处理待办事项。 作为标题 假设,此应用使用了蒸气3 Web框架。 我选择此Web框架,因为我将使用最新的Swift网络堆栈,更好地称为Swift NIO(2018年3月发布)。 Cloud Foundry(CF)的概念之一是对“ 12要素应用程序”模式的大力支持。 CF希望您遵循此模式,并按照“ 12因子应用”网页上主题3所述从环境变量读取所有配置。 在我的情况下,我必须提供一个数据库配置(PostgreSQL),并且对于cf运行时,我必须读取套接字配置,这两者都将通过环境变量提供。 服务器套接字的环境变量名称基于cf运行时。 为此,运行时使用标准变量VCAP_APP_HOST和PORT提供套接字配置。 为了简化本地开发设置,我采用了相同的方法并重用了它。 对于数据库,我使用自己的一组环境变量,并在所有变量前面都加上了PSQL *。 对于我的本地开发,我将所有内容都配置为本地资源,将套接字配置为127.0.0.1:8080,并将数据库配置为本地PostgreSQL数据库。 在CF运行时中,总是为我提供套接字配置,但是数据库变量必须由我自己提供,因此我必须将带有cf set-env的PostgreSQL变量添加到我的CF应用运行时中。 这样,我可以从cf环境中读取托管云PostgreSQL数据库实例的配置,就像在本地计算机上一样。 bash $ cf set-env snatch-todos PSQLHOSTNAME psql.clouddb.hostname.com bash $ cf set-env snatch-todos PSQLPORT 5432 bash $ cf set-env snatch-todos PSQLUSERNAME“ xxxxx” bash […]

哈希表寓言

在Swift中从头开始创建哈希表 理论 哈希表是一个强大的数据结构,大多数开发人员每天都在有意或无意地使用它们。 在Swift字典中是哈希表。 我们应该了解它们是如何工作的,以便我们可以最好地使用它们。 在本文中,我将介绍如何在Swift中制作哈希表的简化版本。 我建议,如果您想了解哈希表的内部工作原理,请遵循并与我一起做一个! 什么是哈希表,我为什么要关心? 哈希表是一种使用键存储和检索值的数据结构。 例如,我可以有一个名称和年龄的哈希表: myHashTable.set(值:30,forKey:“ Manny”) myHashTable.getValue(forKey:“ Manny”)//应该返回30 哈希表的特别之处在于,在正常情况下,我们可以使用键来获取值,而无需遍历数组。 如果我们有一个键值对数组,则必须在整个数组中搜索以找到特定的键值。 这通常称为O(n)访问时间,这意味着获取值的时间基于元素的数量(n)是线性的。 由于哈希表的强大功能,我们可以近似地将值取回O(1)(恒定时间),这使得哈希表非常适合存储我们要基于唯一键快速检索的数据。 重要的是要注意,哈希表不能保持顺序,并且不如用于遍历元素的数组好。 那么,这是什么O(1)法术? 假设您将衣服堆成一堆。 每天早晨,您将不得不在堆里过筛以寻找当天的服装。 现在想象一下,您拥有的所有衣物都有一个抽屉,而这些物品总是放在同一抽屉中。 您将能够非常快速地检索每件衣服! 过于简单的解释是,使用魔术哈希函数,我们的密钥被映射为整数。 该整数将对应于数组中的索引。 这个概念可能看起来像这样: 那么魔术散列函数会是什么样子? 也许为了生成字符串“ Manny”的哈希值,我们可以获得每个字符的整数表示并将它们加在一起。 因此,“ Manny”的哈希值可能等于515。您可能会意识到,由于交换属性“ Manny”将具有与“ Mynan”相同的哈希值。 进行字符串哈希处理有很多更好的方法,但最终它们都只是将字符串转换为整数。 请注意,键不必是字符串,它们可以是任何具有哈希函数的对象。 在理想情况下,每个键都将具有唯一的哈希值,但实际上这是不可行的。 仅根据字符串大小,可能会有数十亿个排列。 如果我们的字母表中只有小写字母,则五个字符的字符串的排列将为26⁵! 想一想,如果您的每件衣服都在自己的独特抽屉中,那么您将需要多少个抽屉。 那么,如何将所有可能的键唯一地映射到数组索引? 我们不! 碰撞和单独链接 哈希函数的目标是将所有可能的键均匀分布到整数范围内。 通过一点数学,我们可以减少这些整数,以便将它们包含在数组大小内。 这将不可避免地导致我们的某些键解析为相同的索引,但不要担心,有几种方法可以处理冲突。 什么是最好的解决方案,让您只用几个抽屉就能穿衣服? 如果您回答将成组的衣服放到抽屉里,那么您是正确的。 如果您只是大堆衣服,那么您的早晨一定很O(n)。 在这篇文章中,我选择使用称为“单独链接”的碰撞解决方案。 通过单独的链接,我们仍将使用键的哈希值来获取数组中的索引。 但是,这次我们将在该索引处有一系列键值对。 这使我们可以将可解析为相同索引的键值对放在可搜索链中。 为了简单起见,我们将使用数组来实现这些链(通常使用链表)。 这是“单独链接”的样子: […]

是孩子们的“快速游乐场”吗?

老实说 第一次在iPad上启动Swift Playgrounds时,我回到了我的四年级老师Mead夫人第一次给我看电脑的时候。 当然,“学习代码”教程比使用徽标要酷得多。 而且,Byte比我年轻时的乌龟还酷,而且更具表现力,但是感觉是一样的。 这种兴奋是我很多次都没有感受到的,iPhone的引入是一个明显的例外。 Playgrounds Books示例很酷,我期待孩子们学习使用此工具进行编码。 不过,那只是冰山一角。 例如,这是Playgrounds支持的部分框架列表: AVKit 帐目 音频单元 加速 音频工具箱 AVFoundation 核心文字 CF网络 核心图片 CoreGraphics 核心数据 核心音频 核心运动 核心位置 核心蓝牙 达尔文 调度 外部配件 GLKit 游戏套件 游戏控制器 图像IO 金属套件 金属性能着色器 媒体播放器 MobileCoreServices 多点连接 OpenGLES 相片 摄影套件 石英核心 快速浏览 重播套件 社会的 SpriteKit Safari服务 WebKit Swift Playgrounds是为孩子而设计的…… Swift Playgrounds也适合我。

如何在iOS应用中利用验收测试

与后端开发等其他领域相比,移动开发是一门新兴的学科。 由于苹果很长一段时间都不关心测试,因此社区开始进行测试的时间很晚。 最初,我们有单元测试工具,我研究过Swift中的单元测试和Objective-C中的单元测试。 进行单元测试很棒,但是顾名思义,它只是测试单元。 因此,我们继续寻找并找到了黄金大师测试。 另一个提高我们质量的好工具。 最后,我们可以确保我们的用户界面是正确的。 但是拥有正确的UI是否意味着我们的应用正在执行应做的事情? 那么,我们应该如何测试整个应用程序呢? 我们已经进行了UI测试。 太好了,我们可以确保我们的应用正确无误。 为我们的应用程序编写了一系列测试,大概两年了(包括2个iOS和Xcode更新),我们意识到: UI测试缓慢,不稳定且难以维护 这么结束了吗? 我们必须忍受这些缓慢的测试,还是可以以某种方式改进它们? 让我们看一下UI测试为什么会这样。 UI测试 速度 Apple在UI测试中的解决方案是运行两个单独的进程。 每当测试请求XCUIElement时,TestProcess都会从原始应用程序获取整个应用程序状态。 然后它将执行整个状态的搜索查询,并列出所有匹配的元素(通常只有一个,但是在找到第一个元素之后仍会继续)。 知道了所有这些步骤之后,我们就可以知道在此架构内的时间: 将AppState创建为XCTest的树 将AppState传输到TestProcess 查询整个AppState 使用Xcode 9,情况发生了一些变化。 它不是在TestProcess中执行查询,而是传输查询,而应用正在执行它。 此外,您可以使用“ .first”在树中找到第一个元素,然后立即返回。 这使UI测试更快,但是并没有我们希望的那样快。 而不是运行60分钟,他们可能需要52分钟。缓慢测试的另一个原因是动画。 您无法检查它们是否为真,因此可以选择停用它们: UIView.setAnimationsEnabled(_ enabled: Bool) 我不喜欢这种方法,因为您需要在生产代码中实现它,并且可能只有使用动画时才会出现错误。 最后,您将不得不考虑收益并决定是否要冒险。 稳定性 研究了速度问题后,为什么UI测试会出现问题? 由于测试是在自己的流程中运行的,因此我们知道它们必须同步。 但是为此,过程本身需要相互查找。 只要不是这种情况,测试就不会在您的应用程序内无故失败。 这不仅可以在测试的第一次开始时发生,而且可以在中间发生。 每当您的应用重启时,都需要找到该应用进程。 此外,有时由于意外的应用程序行为,导致测试运行不同步。 这可能只是系统对话框弹出而您不处理它,或者网络连接中出现小错误,但最终结果可能是毁灭性的。 知道我们的测试不稳定,就意味着每当运行失败时,就需要进行无休止的调试。 这可能是测试,但通常只是某种时机,(在测试内)意外行为,或者只是Xcode起作用。 但这仍然很耗时。 可维护性 在“屏幕对象”中,我们深入探讨了如何维护测试。 即使我们尝试坚持使用它们,我仍然经常看到与应用程序本身无关的代码。 .tap()之类的函数调用可防止测试松动。 用户界面的微小变化可能会杀死该屏幕上的所有测试。 […]

如何在iOS中自动检测内存泄漏

iOS开发人员面临的两个最大问题是泄漏或保留周期。 两者都可能给应用带来一些弊端,例如高内存消耗,随机崩溃,性能下降等。 因此,在Wolox,我们决定研发技术来避免这些问题,开发出功能强大的软件,并最终使开发人员的工作变得更加轻松。 什么是泄漏? 当给定的存储空间无法被系统恢复时,由于无法告知该存储空间是否在实际使用中,会发生内存泄漏。 在iOS中产生内存泄漏的最常见问题之一是保留周期。 当我们在两个或多个对象之间进行循环引用时,就会发生这种情况。 保留周期可防止释放这些对象使用的内存,即使这些对象的创建者释放了它们。 例如,如果我们有一个班级人员和一个班级公寓(如下所示) Swift和Objective-C都有一个引用计数器(ARC),负责释放未引用的内存(换句话说,就是不使用的内存)。 此过程通过计算每个对象具有的强引用来工作。 强引用将引用计数器增加一,而弱引用则根本不增加计数器(当对象的引用计数达到零时,它们会将值设置为nil)。 当实例具有零引用时,它将被释放。 但是,ARC无法检测到保留周期。 在我们的情况下,我们只有强大且交叉的引用,因此,这两个实例将永远不会被释放。 如何修复泄漏? 我们所能做的就是声明一个引用为弱引用,另一个声明为强引用,因此,循环引用被破坏了。 发现此类问题的两种常见情况包括: 案例1:关闭 情况2:委托模式 当一个对象的强引用(我们称其为ObjectA)强烈引用另一个对象(我们称其为ObjectB),而ObjectB强烈引用该闭包时,则可能发生由闭包创建的保留周期。 在下图中,我们可以看到所描述场景的表示。 情况1:可以使用`unown`或`weak`关键字来解决由闭包引起的保留周期,从而破坏循环引用。 另一方面,当未将委托声明为对委托具有强引用的弱对象时,可能会发生使用委托模式(情况2)引起的保留周期。 例如,如果我们有一个视图控制器实现了另一个对象的委托,而这个委托没有被声明为弱对象,我们将得到如下图形: 有关内存管理的更多信息,请查看以下链接:http://krakendev.io/blog/weak-and-unowned-references-in-swift 结合分析仪 在Wolox,我们使用MVVM模式使苗条的视图控制器更加有序,更小并且分开职责。 结合分析仪是我们用来检测泄漏的最早工具之一。 此过程包括评估在视图控制器中实现的绑定数量和未绑定数量之间的差异。 通过绑定,我们的意思是将视图模型的属性连接到控制器的视图出口和动作。 使用这个数字,我们可以区分发布的视图控制器和视图模型与未发布的视图控制器和视图模型。 我们必须小心,不要检测到由寿命长的对象(在应用程序的整个生命周期中保持活动的对象)引起的虚假内存泄漏,例如管理选项卡栏的视图模型。 为了做到这一点,我们有一个“注册表”来跟踪这个长期存在的对象。 该注册表是使用包含该长期对象的标识符的单例数组实现的。 另外,每次执行视图模型的绑定或取消绑定时,我们都需要注册和删除。 为此,我们还有一个带有结构数组的单例,其中包含我们注册的视图模型的数据。 此外,该类将分析是否删除了注册为“长期存在”的视图模型,该模型不应发布。 有关更多信息,请查看:带情节提要的ViewControllers中的ViewModel注入 完成registerUnbinding和registerBinding函数之后,我们可以注册绑定或取消绑定视图模型。 该方法如下: 最后,我们需要添加一个按钮来触发该工具。 实施新功能后,我们可以使用它来运行BindingAnalyzer来检查应用程序内是否存在任何泄漏。 绑定分析器工具是我们在Wolox使用的一种工具,可以产生出色的结果。 即使使用此工具,仍然有可能发生泄漏,因为每次代码库发生更改时,我们都必须手动运行该工具。 使用单元测试自动检测内存泄漏 绑定分析器工具引起的问题使我们思考如何使用单元测试自动检测泄漏。 通常,当我们添加新功能时,我们会测试其行为,但是是否会在添加新漏洞时进行测试? 在Wolox,我们将泄漏测试用于诸如视图控制器和视图模型之类的功能。 使用该技术可以省去手动检查泄漏的过程。 一种实现方法是使用Nimble和Quick。 如您所见,测试由两部分组成,第一部分负责检查视图控制器和视图模型中是否都存在泄漏。 给定一个视图控制器工厂,将创建一个引用以前模型的新控制器。 因此,它必须对视图控制器以及视图模型具有弱引用。 现在,删除与视图控制器关联的强引用并隐藏其视图。 […]

故事板:已批准,但不推荐

自从在iOS 5中引入情节提要板以来,关于为iOS应用程序构建界面的最佳方法的争论一直很多。 尽管UI的可视化编辑器具有许多优点,但是许多开发人员拒绝使用它们,因为它们无法应对复杂的导航和UI层次结构。 Apple通过向OS X引入情节提要来大力鼓励使用情节提要。实际上,Apple Watch现在已要求使用情节提要,并且所有iOS项目模板默认都使用情节提要。 Xcode的最新更新甚至比以前选择在新项目中退出情节提要更耗时。 在Maxwell Forest,我们禁止使用情节提要板来支持程序化布局。 虽然我们可以看到将故事板用于小型项目和单独开发人员的好处,但我们发现在较大的团队环境中,故事板有很多缺点。 紧随其后,了解使用情节提要的利弊很重要。 长处 1.可视化 情节提要是出色的可视化工具,可封装视图表示和它们之间的过渡(segue)。 组织良好的情节提要可以轻松地从高层次了解应用程序流,功能以及应用程序中的视图如何协同工作。 这种可视化可以使刚接触项目的开发人员更容易掌握最新信息。 一个有效用于简单应用程序的情节提要 2.简单性 故事板也非常适合初学者,因为它们易于设置,并且只需最少的代码即可实现许多功能。 即使没有对自动版式的全面了解,简单的界面也可以快速实现,并且拖放界面对于人们学习如何开发比通过编程方式创建版式更加直观。 3.原型制作 Interface Builder具有强大的实用性:它可以使开发人员在没有设计背景的情况下设计出外观合理的UI。 它还允许熟练的设计人员对应用程序进行原型设计,并在工程师充实数据模型并微调UI细节之前将其显示给利益相关者。 弱点 情节提要的简单性和易用性很好,但是在开发更复杂的应用程序时,这也是它们最大的弱点。 在开发复杂的视图层次结构和交互时,尤其是在团队中工作时,Interface Builder的文件结构,工具和稳定性远非理想。 1. Interface Builder的稳定性和怪癖 Interface Builder已经走了很长一段路,但是仍然有其缺点。 例如,它不能在单个情节提要文件中处理大量屏幕,从而产生滞后并经常导致Xcode崩溃。 往返于关键视图控制器之间需要大量测试的应用程序流程可能很快就会陷入混乱,使在情节提要图板之间难以导航。 使用时,Interface Builder中的大多数操作都需要100%缩放,这进一步降低了可用性,尤其是在视图控制器之间拖动以创建序列时。 通过将情节提要分解为多个文件可以缓解这些问题,但是仍然难以管理,并且降低了情节提要作为可视化工具的有效性。 用于复杂UI的情节提要板可能很快变成一团糟 2.没有清晰界面的代码划分 当开发复杂的UI时,将实现分成两个文件,视图控制器和情节提要会使调试和维护变得烦人。 任何工作都需要在代码编辑器和Interface Builder之间进行上下文切换。 在繁忙的大型故事板上,这可能会令人厌烦并且令人生厌。 3.可重用性 可重用性在软件项目中对于减少重复,增加可维护性和错误至关重要。 故事板鼓励使用重复的视觉设计,并手动更改单个元素而非元素类别的属性。 为了使用情节提要板达到可接受的可重用性水平,必须将它们与NIB或程序化UI设计结合使用。 4.单个文件和合并冲突 对于任何严肃的开发,情节提要的最大缺点是它们是包含应用程序多个视图的单个文件。 在团队合作和使用源代码管理时(这应该一直如此),这会产生戏剧性。 只有一个文件意味着合并冲突要普遍得多,当必须使用XML处理冲突时,合并冲突会变得很混乱,这在使用Interface Builder进行更改后会变得非常棘手。 烦人的是,如果开发人员使用不同版本的Xcode,仅打开情节提要板文件将创建更改,这将导致合并冲突! 可以说,通过合理的任务管理可以使合并冲突最小化,但是大多数应用程序的大部分UI流程都集中在几个主屏幕上,并且大多数情况下,多个开发人员将在彼此非常接近的区域工作。 […]

关于Swift镜像的思考

Swift镜像是Swift语言中功能最强大但尚未使用的功能之一。 使用动态提取类实例的元数据的能力 令人惊讶的是,Swift镜像非常简单。 下面的实现动态读取与Flower结构关联的属性,并将其打印在输出窗口上。 但是,我们要做的不只是在输出窗口上简单地打印它。 我们可以使用Swift镜像技术为简单的CRUD(创建读取更新删除)操作创建通用数据访问层。 这是使用Vapor SQLite提供程序的简单通用保存功能。 魔术发生在prepareInsertSQL函数中,该函数利用Swift镜像创建所需的sql。 在prepareInsertSQL中,我们动态创建插入实体所需的所需SQL。 用法如下所示: 现在,我们创建了一个非常基本的通用“保存”函数,该函数可以使用Swift镜像的功能来动态生成SQL并将数据插入SQLite数据库。

Swift中更好的承诺

一个简单的承诺 异步代码很常见,但是如果我们仅在一个地方添加更多的异步调用,我们将得到大量的方法和十个级别的缩进。 换句话说,我们将成功实现回调地狱。 当然,我们可以做得更好! 我们可以尝试将其拆分为几个功能……但这可能会导致其他问题…… 这种方法的缺点是很难跟踪控制流。 从方法签名来看,不清楚哪个方法被调用。 如果方法调用是实现方法职责的一部分,那么这是一件好事,但是如果它调用了流程管道中的下一个项目,那么它很快就会变成可控流地狱。 这就是为什么我们首先要有回调! 我们希望在一个地方定义高级控制流,然后将实现细节放在其他地方,并适当地封装它们。 一种可能的解决方案是使用诺言。 那么, 什么是诺言? 承诺(也称为“未来”)是指用于同步程序执行的构造。 他们描述了一个对象,该对象充当最初未知的结果的代理 ,通常是因为其值的计算尚未完成[1]。 我为什么要使用诺言? 保持代码干净和结构良好。 这是避免回调地狱和控制流地狱之类的简单选择。 我们始终希望代码缩进尽可能合理 ,而诺言是实现这一目标的好方法。 答应过地狱? 根据使用方式的不同,promise可以简化代码,但也会使代码难以理解。 在进行代码审查时,我基本上遇到了一件奇妙的事情: 相当多的工作,不是吗? 乍一看,这是有问题的,因为代码在美学上看起来并不令人满意(在我看来,这是判断代码质量的很好指标)。 不幸的是,使用promise退化为类似代码的代码并不少见。 所以有什么问题? 很难说出.then块的作用,它们可能变得非常庞大。 它也违反了单一责任原则[2],因为每个块都有单独的责任,但是它们都包含在一个方法中。 我们遇到的另一个问题是缺乏抽象层的分离。 一个方法应该只包含占据一个抽象层的代码,在我们的例子中,我们可以说有两个:1.管理过程流程的代码,以及2.构成过程步骤的实现细节。 更好的承诺 一种简单的解决方案是在每个块的开头添加注释,以描述其功能。 更好的解决方案是编写自我记录代码 ,在这种情况下,这意味着将程序包提取到单独的方法中 。 只要确保正确命名方法即可 。 而且您还需要正确命名方法(我已经说了两次,因为它很重要,以防您想知道)。 最初的问题是,我们无法轻易分辨出发生了什么,而用模糊的内容代替它并不会带来太大帮助,对吗? 即使块的内容包含很多代码,如果将其包含在方法中,它也更易于管理,并且我们还解决了抽象层问题。 答应天堂? 由于Swift函数是一流的对象,因此我们可以采取进一步的措施。 这意味着我们可以将它们传递到其他需要关闭的函数中 。 promisseHell示例将变为以下内容: 我们可以选择几种承诺库。 最受欢迎的可能是PromiseKit [3]。 就我个人而言,我更喜欢then框架[4],因为它的方法不需要标签,这使它更加简洁(即在那时我们将编写.then (handler),而在PromiseKit中将是.then (execute:handler) )。 任一种都可以,但是您将需要根据所选框架的期望编写略有不同的代码。 […]

使用协议掌握泛型:规范模式

如果您正在阅读本文,那么您很可能已经知道使用泛型的好处。 您想要减少编码。 您希望编写的代码能做更多。 您以前曾尝试在编程中使用泛型,但有可能使您从未想到的挫败感达到顶点。 你不是一个人。 能够以一种通用的方式工作,但是仍然可以成功地推断出您的类型可能很棘手。 幸运的是,将泛型与协议的使用结合起来确实很有效,而且一旦您学习了少量技术,您就会发现这棘手的事情实际上并不是那么棘手。 为了探讨该主题,我们将构建和完善一个针对以下四个方面的规范设计模式: (我希望)这些技术将成为您的编码库中的主要内容。 顺便说一句,这种模式是解决开放-封闭原则的极好方法。 我强烈建议您熟悉的东西,以及我最近 在这里 写的一些东西 。 过滤产品 我们的假设情况是,我们有一个称为Product的实体,并且我们希望能够按某种规格过滤一系列产品。 首先让我们看一下Product : 这是有道理的,因为我们的协议类型Specification是泛型的,而且我们可以可行地添加一个规范实例,该实例的关联类型不是Product 。 我们需要做的是检查items参数中的类型是否与spec参数中的类型相同。 让我们尝试一下。 技术2:检查通用实例上的条件 我们可以使用以下模式在函数声明的开头执行检查: func someFunc (参数:T)其中T.someProperty == self.someProperty 请注意,我们在输入参数之前使用了典型的占位符语法 ,以表明我们的函数正在使用泛型类型。 为了能够访问T实例的内部,我们需要首先说一下实例的类型是 ; 在这种情况下, T的类型为SomeType 。 接下来,我们需要表明我们要检查使用where关键字完成的条件。 最后,我们可以访问T.someProperty类型的属性并执行布尔检查。 现在,只要通用类型为SomeType且其值someProperty与我们自己的类型相匹配,该函数就可以使用。 考虑到这一点,让我们构建一个通用过滤器 ,该过滤器可以在规范associatedtype与物料类型匹配的情况下接受任何物料和任何规格。 现在,代替将specs参数类型设置为Specification ,我们可以改用Spec,因为它具有与我们Filter (第5行) 中的那个相关联的关联类型 ,这是安全的。 创建协议后,实现Filter本身非常简单。 我们遵循协议(第9行),设置类型别名 (第10行),然后filter(items:,specs 🙂神奇地将其自身设置为正确的类型。 现在,我们可以使用我们先前制作的Product实例进行测试 在这一点上,我们可以肯定地说一切正常。 使用我们当前的设计,创建更多的过滤器和规格是一项非常简单的任务。 但是,让我们看看是否可以进一步完善它。 目前,我们的ColorSpecification实体仅适用于Product类型的实例。 […]