Tag: 编程

不要使用Swift,YET编写框架/库。

仅供参考。 如果您的框架/库是开源的并且将作为源代码发布,则可以跳过此文章。 Swift是一种年轻而强大的语言,自从2014年WWDC宣布以来,发展迅速。作为iOS应用程序开发人员,我将使用Swift构建的应用程序提交给App Store后就从Objective-C切换到Swift。 但是用Swift制作框架而不是应用程序仅几个月,我遇到了一个严重的问题。 “使用Swift X编译的模块不能由Swift Y编译器导入:” 这是令我沮丧的问题。 我的框架是封闭源代码,因此我将代码编译并打包到框架中,然后移交给客户。 但是一些客户报告说由于上述错误,他们无法使用我的框架来构建他们的应用程序。 深入研究问题之后,我意识到除非应用程序的构建环境(Swift版本和Xcode版本)与Swift框架所构建的构建环境完全相同,否则无法对其进行编译。 那是因为Swift尚未在ABI上稳定。 Swift尚未在ABI上稳定。 ABI(应用程序二进制接口) 您听说过ABI吗? 坦白说,我还没遇到这个问题。 那么,什么是ABI? ABI是应用程序二进制接口。 您必须熟悉什么是API(应用程序编程接口)及其用途。 ABI是API的低级版本,它定义了两个二进制模块之间或通常在问题及其操作系统之间的接口。 与API相比,这是对ABI的很好解释。 简而言之,通过在OS中具有稳定的ABI,为较早版本的OS编写的程序仍可兼容并在较新版本的OS中运行。 但是Swift不是ABI稳定语言,这意味着Swift标准库没有嵌入OS中。 而是将它们嵌入每个应用程序中。 您可以在Xcode构建设置中看到“ Always Embed Swift Standard Libraries选项,如果在Objective-C项目中启用此选项,则应用程序的大小将增加几兆字节。 因此,通过要求在每个应用程序中嵌入Swift标准库,即使Swift不是ABI稳定的,iOS应用程序也可以避免兼容性问题。 Swift 5有望具有ABI稳定性 为什么Swift无法稳定ABI? 这是因为Swift仍然是一种过早的语言,它通过频繁的版本更新来进行许多更改和验证。 如果该语言在太早的阶段就定义了它的ABI,那么就不能以这种方式迅速地开发和改进它。 但是在今年,随着Swift 5的更新,它有望为ABI稳定做好准备。 之后,我们可以使用Swift自由编写框架,库和SDK! 好极了! 我的解决方案? 遗憾的是,我没有时间等待Swift 5🙁我可以做出3种选择。 为每个客户的构建环境构建框架 将其设为开源并按源代码进行分发 用Objective-C重新编写 我的选择是? 第3条。将Swift代码重新编写到Objective-C并不是一件繁重的工作,但是切换回旧的和缺乏安全性的语言感觉很糟糕。 实际上,我在翻译工作中没有犯错。 将为此写单独的帖子。 TL; DR Swift正在开发尚未稳定的语言。 因此,您不应使用Swift编写框架/库,因为它可能与客户的应用程序不兼容。 […]

Swift解决方案:装饰器模式

Swift Solutions是涵盖设计模式的一系列文章。 在每篇文章中,我们讨论该模式是什么,何时应用以及如何以Swifty方式实现它。 在Swift解决方案的此迭代中,我们将在Swift中实现Decorator Pattern。 该模式提供了一种向应用程序添加功能的有用方法,并且是每个开发人员都可以在其套件中使用的出色工具。 我们将在方法上做到周密,以免材料发粘。 我们将在Swift中定义,说明和编码模式。 最终,您将了解该模式是什么,何时应用以及如何编码。 让我们直接进入定义! 限定 装饰器模式可动态修改核心对象的行为,而无需更改其现有的类定义。 装饰器模式有两个主要组成部分:一个将修改其行为的核心对象,以及一个在核心对象中带来更改的行为的装饰器对象。 这两个组件可以共同实现两个主要目标: 行为修改 …动态实现 行为修改 该模式的目的是在不修改其现有类实现的情况下更改核心对象的工作方式。 这是通过用装饰器包装核心对象来实现的,装饰器可以增强核心对象的默认行为。 装饰器从字面上完全“装饰”(修改)了它们包装的对象。 装饰器增强了核心对象中已经存在的行为。 它们与核心对象共享相同的接口,但提供不同的实现。 结果,客户端无法区分是使用装饰器还是使用核心对象:它们在两者上调用完全相同的函数/属性。 此共享接口允许装饰器充当中间人,拦截对核心对象的调用并为客户端提供自定义行为。 动态修改 装饰器不只是简单地修改行为,而是动态地修改行为。 换句话说,它们更改了对象在运行时(而不是编译时)的工作方式。 编译时行为修改很简单:只需要​​直接更改对象的类定义或子类并覆盖其默认实现即可。 但是,由于以下原因,可能不希望使用这些选项: 可以将核心对象类声明为final ,这可以防止子类化。 该类可以在第三方框架中定义,对它的任何更改都将在下一次框架更新时丢失。 可能存在许多所需的修改,并且可以将几种行为链接在一起以产生复杂的组合。 为每种可能的组合创建一个子类将导致类爆炸。 对现有类的更改可能会导致意外的副作用,从功能中断到崩溃。 当继承脆弱且未完全理解的旧版代码库时,尤其如此。 所有这些缺点使动态方法成为有吸引力的替代方法。 我们没有将具有不同行为的核心对象的多个变体硬编码,而是将每个行为分成了自己的专用包装器类。 然后,通常在用户输入期间,将这些专用包装器在运行时应用于核心对象。 用户选择他们的自定义项,然后我们应用包装器以产生所需的结果。 这是一种灵活的方法,因为行为的组合是通过根据需要增量应用包装器来实现的。 说明 我们将使用赛车游戏作为插图。 在此游戏中,玩家在每次比赛前选择车辆。 选择后,他们可以自定义车辆的轮胎,变速箱,发动机等。 这些自定义通过更改其统计信息来影响车辆的行驶方式。 在我们的示例中,统计信息将是速度和牵引力。 玩家可以选择更换零件以更好地适应他们的喜好。 例如,玩家可以选择将默认轮胎更改为越野轮胎,这些轮胎的抓地力更好,但速度却有所降低。 实行 让我们使用赛车插图来实现该模式。 protocol Transporting { func […]

Swift数据结构:堆

堆是一个非常整洁的数据结构,可以对最小值或最大值进行优先级排序。 它与树的分层和节点结构有些相似,但是由于它是线性的,因此可以轻松地存储在数组中。 对于视觉人来说,如果我们将其绘制出来,这就是堆的外观: 上图显示了最小(最小)堆,该堆优先排列顶部数据集中的最小值作为根父节点。 在下面,存在值为3和8的节点,它们分别作为父节点的左和右子节点(值为2)存在。 值为3的节点的左子节点为4,右子节点为7。此过程将一直进行下去,直到所有数据集中的值都已布置好为止。 请注意,这些值通常随从顶部到底部的增加而增加。 它们本身不是从左到右组织的,而是按行组织的。 第1行在根节点的位置包含值2。 第2行的左,右子节点的值3和8都大于2。第3行包含了节点3和8的左右子节点。如您所见,两个节点的子节点在值。 希望您开始注意到一种模式。 最小堆中的每个父级可以有0、1或2个子级-所有这些子级的值都大于父级。 最大堆的工作方式完全相反。 堆利用索引系统(如数组)来确定值的顺序及其在堆中的位置(从索引0开始并向上计数)。 如果我们从较早之前引用可视堆图,则索引从上到下,从左到右递增,如下所示: 到目前为止,您所看到的示例都显示了静态数据。 我们没有添加或删除任何项目,但是最重要的是我们可以更改Heap中的数据,否则此数据结构将毫无价值。 在Swift中,我们将首先创建一个名为MinHeap的结构, MinHeap所示: 遵循堆的规则,您可以看到从上到下的每一行通常会逐渐变大,并且每个父节点的子节点都大于其自身。 调用peek()返回您期望的2值,而调用poll()在删除第一项后重新排列堆。 若要查看实际效果,请将以下代码添加到MinHeap结构的底部,然后再次调用dump(myHeap.items) : 我们做到了! 现在,我们已经成功地构建了一个最小堆,它对传入的最小值进行优先级排序。删除值后,堆将重新排序,因此下一个最小值位于顶部。 但是在现实世界中可以在哪里使用呢? 在Github Gist上获得完整的源代码:https://gist.github.com/kalub92/d269ba6b2bf05ca7dcbaae64b4ff7a2d 想象一下,您正在为儿科急诊室建立患者摄入系统。 医院希望优先考虑最年轻的患者,以便他们能够尽快接受护理。 想象一下,有几个6、7和8岁的孩子在候诊室,但一个病得很重的1岁孩子进入候诊室并经历了摄入过程。 在时间上,1岁应排在第4位,但该医院希望首先照顾最年轻的患者。 如果在患者摄入计划中使用了Heap,则1岁的孩子会马上去找儿科医生见面。 这是一个特定的用例,但我想知道您的想法。 您认为堆可以在现实世界中有用地实现在哪里。 在下面发表评论,让我们知道! 如果您准备加入我们并了解有关Swift,数据结构和iOS开发的更多信息,请查看Devslopes并注册我们的Apple坡度,以学习构建自己的应用程序和启动基于应用程序的初创公司所需的所有技能。 。

学习Swift-第一个成功!

在继续学习Swift编码的过程中,我已经达到了第一个成功点。 完整且有效的游戏。 它是CandyCrush的副本,是在此非常有用的教程的帮助下制作的。 我花了几天的时间来完成课程。 我在这里和那里都遇到了一些主要与错别字或代码错误有关的问题,但是解决这些问题是值得的。 尽管我仍然无法从头开始创建自己的应用程序,但按照这样的指南进行操作,并继续阅读Apple的“使用Swift开发应用程序”,这是通往未来基础的道路。 CookieCrunch教程给我带来了一些挑战,我将尝试自己添加一些挑战,但是我不想花太多时间在此上,所以如果我不马上解决,我会参加另一个指导课程,以帮助我继续建立这个基础。 通过这些教程的工作确实使我大开眼界,了解创建我们每天使用和喜爱的应用程序涉及多少工作。 我羡慕那些希望并最终获得他们的大发布的独立开发人员。 我希望有一天能成为发行版的风口浪尖,但是即使那样,发行版也只是我们面临的第一个战役-获得可见性是一个“万无一失”的蠕虫。

字符串中最常见的字符和字符串的排列

上周,我非常荣幸地访问了Spotify办公室,在那里我有机会与那里的一些很棒的工程师聊天。 我认为写自己的经验对我来说是有益的,最重要的是,将其记录为改进的平台。 在本文中,我将介绍与字符串有关的算法,特别是在字符串中搜索字符,并创建给定字符串的所有可能排列。 字符串中最常见的字符 首先,让我们从查找字符串中最常见的字符开始。 例如,字符串“ Manhattan”应返回“ a”,因为字母“ a”是最常见的,计数为三。 创建函数时,我的目标是始终弄清楚参数是什么。 在这里,我们正在输入一个字符串,并且我们的函数应该返回一个字符。 接下来,我的方法是将字符串分成字符数组。 通过这样做,我可以遍历字符数组并检查哪个字符的频率最高。 为了进行检查,我们必须具有某种计数器来跟踪哪个字符的频率最高。 另外,在搜索该字符时如何访问? 最初,这让我感到难过,直到我不得不翻阅数组并更多地考虑其他可能的数据结构。 在这种情况下,我们可以使用字典,其中我们的键是一个字符,而我们的值是它在单词中出现的频率。 以下是我最终得到的结果: func returnMostCommonCharacter(string:String)->字符{ var个字符= Array(string.characters) var dict =字典() var maxCharacter =个字符[0] var maxCount = 0 字符中的字符{ 如果让我们看到CharCount = dict [character] { dict [character] = seenCharCount + 1 如果dict [character]! > maxCount { maxCharacter =字符 maxCount = dict [character]! […]

使用Swift枚举更安全,更易读的代码

如果您已经构建了一段时间的iOS应用程序,则可能已经编写了一些View Models。 在本文中,我们将以一个视图模型为例,说明如何使用Swift枚举编写更安全,更易读和更可扩展的代码。 在我们的示例中,我们将构建一个必须表示三个状态的视图模型: 第一个状态是加载状态,表示在我们有任何内容要显示给用户之前正在进行的网络操作。 通常,向用户显示带有某种活动指示器的空白屏幕。 第二种状态是项目(即产品)的同类列表。 内容加载后将显示此状态。 第三状态是刷新状态,表示用户何时手动触发了正在显示的内容的刷新。 在这种状态下,我们希望继续显示以前加载的内容,但还向用户显示一些指示正在进行网络操作以获取新内容的指示。 我们可以构建一种表示这种情况的结构的方式如下: var viewModel:ViewModelstruct ViewModel { var isLoading:布尔 var isRefreshing:布尔 var content:[内容]? } 乍一看似乎很好,但是仔细检查后,很明显,我们的视图模型具有我们实际上不需要的几种状态。 例如,将isLoading和isRefreshing都设为true并没有多大意义,它们应该是互斥的。 您可能会认为这很好,因为您知道这些标志应该互斥,并且可以确保每次将值分配给一个时都将反向赋给另一个。 可能会带来摩擦的地方是不具备该知识的人来对此代码进行一些更改。 仅仅通过查看View模型上的属性,他们就可以假设isLoading和isRefreshing可以同时为true ,或者当isLoading为true时content可以包含元素,这不是我们的意图。 用数学术语来表示,如果我们将content视为布尔值(我们有内容或没有内容),则我们的视图模型具有2³= 8种可能的状态,与我们预期的三种状态相反。 输入枚举 我们如何阐明和执行我们对视图模型的规则? 我们想要实现的是一个数据结构,其可能的状态恰好反映了我们视图的预期状态。 使用Swift枚举,我们可以这样做: var viewModel: ViewModel枚举ViewModel { 装箱 装箱(内容:[内容]) 案例刷新(内容:[内容]) } 查看此内容,我们可以清楚地看到我们的视图应具有三种状态:加载内容,显示内容和刷新内容(但仍显示先前加载的内容)。 这使初次查看此视图​​模型的人更容易了解确切的预期状态。 当我们需要与我们的视图模型进行交互时,以前的代码看起来像这样: 如果viewModel.isLoading { //设置加载界面 } 否则,如果viewModel.isRefreshing { //如果让content = viewModel.content { […]

威伯·斯威夫特

NavigationController对View,View to Presenter,Presenter to Interactor和Router都有很强的引用。 因此,我们让NavigationController包含所有模块。 结果,此解决方案使我们可以在将ViewController从NavigationController中删除后销毁所有模块。 TableViewDelegate和TableViewDataSource 我们建议使用的方法不是唯一可能的方法。 创建它是为了没有更好的东西。 让我们用一个例子来展示它。 这里有一系列的人类对象,它们是通过一些API异步接收的。 人类具有以下特性: –名称:字符串; –年龄:整数; –照片:UIImage; 为了显示信息,UITableView正在使用。 人的名字,年龄和照片被加载到一个单元格中。 VIPER原则之一说View是被动的,但是Presenter不能向View返回任何内容。 视图应具有设置器,Presenter用来显示信息。 问题在于它仅适用于已创建的对象,而不适用于动态创建的对象。 这意味着您不能为每个单元格写一个setter。 这是为什么? 首先,编写应用程序时,单元数是未知的。 其次,为相同的对象编写setter是愚蠢的。 解: 让我们为View内部Human的每个属性创建一个变量,然后为每个新变量添加一个setter。 接下来,在Presenter中创建一个方法,该方法从Interactor获取数据并将其设置为View中的“ currentHuman”变量: 这样,每个单元格都填充有正确的数据。 它破坏了原理,该原理说View除了对UIViews和Presenter的引用外不应有任何其他内容。 但是它遵循另一个规则,并且代码仍然可以测试。 PS:本文由BytePace移动开发人员撰写。 您可以在这里查看他的作品和其他文章:http://bytepace.com/

基础知识:Swift最佳做法

最近,我的伙伴对编程表现出了兴趣,因此自然而然地我推荐Swift作为一种很好的语言。 我花了很多时间亲自研究了Swift的最佳实践,我想确保她确实遵循了整个Swift社区的最佳/常见实践(我认为其中有些是最佳实践),并且没有从一些那里的教程。 即使这些教程为初学者提供了一个很好的起点,我也注意到一些确实困扰我的做法,我建议您反对。 注意 我应该指出, 这要取决于我在编写Swift代码时的最佳做法以及我在社区中的研究中所收集的内容。 我敢肯定会有人不同意,这很好。 我仍然会睡个好觉😉 首先,让我们从合作伙伴搜索初学者教程时遇到的编码实践开始。 我还将介绍与其他开发人员一起进行项目时所经历的一些经验。 1)冒号空间 尽管这看起来很小,因为它只是一个空格,但是它可以大大提高代码的可读性。 推荐的 命名:字符串 不建议 让名字:字符串 命名:字符串 让名字:字符串 简单。 在冒号之后,在类型之前添加一个空格。 2)花括号 当谈到花括号时,通常似乎是一个意见问题。 但是,在浏览互联网上的一些样式指南,Apple文档中的示例以及我个人的看法时,确实有一个赢家。 推荐的 如果isValid && list.count == 0 { …}否则,如果isValid { …}其他{ … } 不建议 如果isValid && list.count == 0 { …} 否则,如果isValid { …} 其他{ … } 要么 如果isValid && list.count == 0 […]

C中的静态库

在上一篇博客文章中,我们了解了编译过程,并且在链接部分中,编译器将二进制代码连接到库,然后返回可执行文件。 现在让我们详细讨论这些库。 首先…什么是图书馆? 井库是一个包含几个目标文件的文件。 好的……但这意味着什么,这些文件是如何到达那里的? 回答这个问题可能会提示其他问题,因此让我们看看C中的静态库是关于什么的。 在这里,我们将学习它们如何工作,为什么使用它们,如何创建它们以及如何使用它们。 他们如何工作? 在编译过程的最后阶段,链接器生成可执行文件时,它还会添加(从库中复制)我们在程序中使用的函数的代码。 该功能是静态库的一部分,我们将其包含在代码中,以便能够使用该功能。 对于静态库中代码中的每个函数,链接器都将在可执行文件中包含该函数的代码。 如果我们在同一个程序中使用很多功能,这将是一个问题,因为对于每个我们包含的功能,程序的大小都会增加,因为我们正在将该功能的代码复制到可执行文件中。 静态链接的另一个问题是可移植性。 那么,当我们移动该可执行文件时会发生什么? 没有! 这就是问题所在。 如果同时对库进行了某些更改或库已被更新,则这些更改将不会反映在我们的程序中,除非我们使用更新后的库重新对其进行编译。 2.为什么我们使用它们? 现在必须怀疑……如果库增加了程序的大小,并且每次对程序进行一些更改时都重新编译它,我们为什么还要使用库? 但是,即使存在静态库的这些缺点; 如果您考虑一下,如果您移动程序并且没有库需要链接,该怎么办。 在这种情况下,代码已经复制到您的可执行文件中,您不必担心。 同时,这使程序的分发和安装更加容易。 但是您可能会问为什么我们使用它们? 您是否曾经在不同的程序中一遍又一遍地编写相同的代码,并且希望有一种神奇的方法跳过该部分。 好吧,这就是图书馆的工作。 您可以将这些代码片段添加到库中,而只需在编译过程中包含一个库,编译器便会在需要时将其插入程序中,而无需一遍又一遍地编写。 现在,让我们看看如何实现这一目标。 3.创建一个静态库 用于创建静态库的基本工具是一个名为“ ar”的程序,用于“存档”,这就是在命令行中的样子。 ar rc lib name_of_your_library_here .a file1.o file2.o file3.o 此命令创建一个名为“ lib name_of_your_library .a”的静态库,并将对象文件“ file1.o”,“ file2.o”和“ file3.o”的副本放入其中。 每个库名称都 以lib开头,以.a扩展名结尾 。 让我们看一些例子。 首先,我们编写一些程序来单独执行一些我们经常使用的任务,并将所有原型都写入头文件中。 现在,为了将所有目标文件放入我们的库中,我们首先需要从所有这些.c文件中创建目标文件,我们使用gcc对其进行编译,但是使用选项-c可以返回相同的文件名,但作为对象文件,扩展名为.o。 现在,那些.o文件是我们要保存在库中的目标文件(在示例中名为my_lib),我们通过使用上述命令ar和’ rc ‘选项来执行此操作,以防万一库不存在被创建 […]

Swift中的Leetcode解决方案和思想—问题554

矩阵和哈希表 难度:中等 录取率:46.5% 你面前有一堵砖墙。 墙是矩形的,有几排砖头。 砖块的高度相同,但宽度不同。 您想要从顶部 到底部绘制一条垂直线,并穿过最少的砖块。 砖墙由行列表表示。 每行是一个整数列表,从左到右代表该行中每个砖的宽度。 如果您的线穿过砖的边缘,则该砖不视为交叉。 您需要找出如何绘制线以穿过最小的砖并返回交叉的砖的数量。 您不能仅沿着墙的两个垂直边缘之一绘制一条线,在这种情况下,该线显然不会穿过任何砖块。 这个问题是相当困难的。 我们需要知道的前两件事是墙的宽度和高度 。 从宽度和高度,我们可以首先创建一个像这样的矩阵: 每个“ 0”表示那里的空间被砖块阻塞。 我们希望矩阵变成这样: 其中1表示两个积木之间的交集(或未交叉)。 因此,我们要做的是在各列之间找到最大的加1。 完整的方法如下所示: 该解决方案的复杂度基本上是O(n²),但是我们首先创建一个矩阵,并且还有其他for循环,这使得运行时间超出了时间限制。 矩阵似乎不是一个好主意,所以让我们以哈希表的方式进行。 在第3行,我们首先创建一个字典,以注意每列未越过多少块砖, 然后我们遍历砖墙,我们知道当柱位于砖之间时,柱上不会有任何砖交叉。 循环时替换mostCrossed变量。 该解决方案的复杂度也是O(n²),但是借助哈希表,显然更快。