Tag: 面向协议

面向协议的UITableViewCells

这篇博客文章展示了如何通过使用面向协议的编程 (POP)作为子类化或组合的更好替代方法来实现一组UITableViewCell变体。 准备? 我们走吧! 给我看看细胞! 在构建我的Cast Player应用程序时,我需要添加几个设置页面,以允许用户在应用程序中进行一些调整,并包括指向“发送​​反馈”表单和一些“关于”屏幕的链接。 这是最终结果: 从上面的屏幕中,我们可以识别六种不同类型的单元格: 总体而言,这些细胞具有以下特征(或行为): 单元格突出显示 显示标题 显示人类可读的字节数 我们如何去建立一组能很好地代表这六个细胞变异的类呢? 第一步是列出网格中的所有单元格类型和所需特征: 从上表可以看出: 所有细胞都没有采用这些特性。 一些特征是某些细胞所必需的,而其他细胞则不需要。 这意味着构建UITableViewCell类层次结构在这里无法正常工作。 实际上,这些单元格类型都不是基础类的理想选择。 该怎么办? 🤔 协议和扩展! 好极了! 幸运的是,这篇关于Swift 2.0中Mixins和Traits的精彩文章可以为我们指明正确的方向。 直观地讲,协议和扩展对于该用例确实可以很好地工作,但是如何在此处正确使用它们呢? 这个想法是我们可以使用协议为每个特征定义接口 ,并提供带有扩展的默认实现 。 然后,我们可以创建非常小的UITableViewCell子类,并使它们仅符合所需的协议。 在另一个名为“面向协议的MVVM简介”的精彩演讲中,探讨了一个非常类似的问题。 让我们看看它是如何工作的! 有了协议和扩展,我们可以编写第一个特征TitlePresentable : 在代码中,这表示为: 注意 : HighlightableTableViewCell是通过子类实现的唯一特征,并且可以用作三种其他单元格类型的基类。 一旦为给定类型选择了基类,则只能通过协议扩展来添加其他特征。 结论 使用Swift协议和扩展作为增加行为(特征)的一种方式,可以在我们的类的设计中取得重大胜利,并有助于保持层次结构的平坦。 🚀主要优点: class媚的阶层阶层 生成的API /类可以更轻松地扩展 与添加和删除代码相比,添加和删除协议一致性更容易 减少代码重复 欢迎反馈 这篇文章中介绍的解决方案对我和我的特定用例都非常有效-我希望这个实际示例可以帮助我比起初时更好地理解如何使用协议扩展。 如果您知道这样做的更好方法,请在评论中告诉我! 注意 :这篇文章首次出现 在 2016年6月18日发布的 […]

如何避免协议导向沉迷于编程

大约一年前,我正在观看2015年的WWDC视频,当时一次特别的会议演讲引起了我的注意。 似乎引起了所有人的注意。 鉴于在过去一年中创建了与Swift相关的内容,许多人已跃入全新的编程范式。 我当然是指大卫·亚伯拉罕斯(David Abrahams)提出的有关面向协议的编程的精彩演讲。 如果您还没有看到它,并且使用Swift编写应用程序,那么绝对值得一试! 认真地做吧。 接下来的事情并不是试图减轻演讲中所呈现的内容的酷感。 如果正确应用协议扩展,它将是一个很棒的工具,它具有许多实际用途,我们将在本文的最后进行介绍。 问题是我们所有人都感到内gui(绝对包括我自己)。 当您拥有闪亮的新锤子时,一切都将变成钉子。 因此,在过去的一年半中,协议一直在敲定,我们的项目是钉子。 我们学到了什么? POOP的起源 它总是以一种天真的渴望学习新事物而开始。 与在圣诞节打开Nintendo 64的包装并整夜陪同兄弟姐妹玩Goldeneye并没有什么不同。 是的,我知道那与我约会的时间。 协议定向痴迷编程是过度使用协议扩展需求的产物,尤其是在不需要它们的情况下。 POOP是对令人敬畏的新工具的一种完全可以理解的反应,但是正如我之前所说的,在编程中所做的任何事情总会有取舍。 仅查看收益是远远不够的。 我们必须始终考虑成本。 那么面向协议编程的权衡是什么? 为什么我们要避免将所有内容重构为协议和协议扩展的冲动? 是什么使POOP便便? 一切都取决于 代码的 可读性 。 痴迷于协议定向的编程如何危害代码的可读性。 与开发人员团队合作时,有效的沟通是成功的关键。 可读的代码可导致更好的沟通。 不可读的代码使团队工作变慢,因为每个人都需要更多时间来弄清楚它的作用。 作为团队中的开发人员,您应该始终努力减少代码对同事的认知负担。 拥有可读的代码不仅仅意味着选择正确的类和函数名称。 与在单个位置中显示适当数量的信息有很多关系。 另一个开发人员应该只查看一个文件就可以弄清楚代码的作用。 这个有一个名词。 我们称其为本地推理。 这意味着人们应该能够推理您的代码,而不必到处移动太多。 它是一种支持预先的显式代码的设计哲学。 当您出于保留局部推理的目的而编写代码时,尝试避免使用复杂的类层次结构和设计,这些层次结构和设计迫使其他开发人员从一个文件跳到另一个文件,只是为了清楚地了解代码的作用。 协议定向痴迷编程是有问题的,因为它使本地推理几乎不可能。 为了更好地理解为什么会出现这种情况,我请您取悦 惊人的爆炸结构 假设您有一个基本的Milkshake结构。 它具有您可能熟悉的一些变量和功能。 现在我们可能不一定同意授予Milkshake结构的功能,但是我们可以同意这里有可读的代码块。 就是说,所有这些都为我们阐明了。 我们不需要去其他地方去了解这个结构的作用。 很容易推理。 如果我们将奶昔的一部分重构为一些单独的协议,将会发生什么? 好吧,这并不可怕。 从技术上讲,它仍然像我们的其他结构定义一样工作。 不幸的是,它的可读性差得多。 […]

Swift中的关联类型

动机 可重用性是软件开发中的重要组成部分。 我认为,软件体系结构中最重要的考虑因素之一就是即将推出的功能的实现时间。 我经常有片刻,然后躺在床上,直到实施了该死的功能后才入睡。 因此,快速的实施时间可以为我节省大量的睡眠时间(这是我绝对需要的)。 继承是重用超类功能的一种方法。 但是事情会迅速发展,随之而来的是复杂性的增加。 幸运的是,我们在Swift中有了面向协议的方法。 但是协议必须是通用的,以便将其定义重新用于不同的类型。 类允许我们指定通用参数。 协议为我们提供了“关联类型” 。 实际示例:用于CoreData处理的CRUD协议 我想为我的CoreData实现创建一个协议。 这将是常见的CRUD (创建,读取,更新,删除)功能。 没有通用协议,我必须在所有地方都使用NSManagedObject,这真的很不舒服,因为我有Xcode生成的子类。 我的实体是: TaskMO和ItemMO 。 我都需要他们的经理班。 对于TaskMO,协议定义必须匹配一次,对于ItemMO,协议定义必须匹配一次。 可以使用“ associatedtype ”关键字来实现。 协议持久{ 关联类型实体 } 如您所见,实体只是一个占位符,而不是具体类型。 具体类型将仅在实现中指定。 使用约束来缩小类型 Persistable协议使我们能够处理CRUD功能。 但是我要确保将其用于CoreData。 我们可以通过向我们的关联类型添加约束来确保这一点。 就我而言,我只想允许在NSManagedObject子类上使用。 此步骤与通用参数非常相似。 协议持久{ relatedtype实体:NSManagedObject } 完整协议如下所示: 编写实现 现在按照您习惯的方式实现类型。 名为“类型推断”的编译器功能会自动检测具体类型的定义。 因此,我们不必明确指定它。 煮咖啡机 凉。 我们创建了一个通用的可重用协议。 但是,让我们深入一点。 我想制造一台咖啡机。 不只是基本的黑咖啡。 我想要一杯很棒的拿铁玛奇朵。 首先,我指定成分: 我的咖啡由主要的咖啡成分和某种牛奶组成。 我将协议命名为“ Drinkable […]

面向协议的编程:一种快速的OOP头痛解决方案

为什么OOP如此受欢迎? 面向对象编程(OOP)是数十年来编写模块化代码的经典策略,并且有充分的理由。 可重用性:编写良好的对象是可以插入其他程序的独立实体。 如果实现了优化的树,则可以在现有项目的任何部分或全新的树中使用它,而无需编写更多代码。 跳过细节 :另一个程序员不需要知道您对象的实现。 如果看到“排序”功能,则可以使用而不必担心代码是什么样。 防错 :我们可以隐藏对类中函数或变量的访问,以防止其他不熟悉我们代码的开发人员创建错误。 其他人只会看到他们需要看的东西。 使大型项目易于管理:大型 项目变得复杂。 OOP使我们可以将一个大问题分解为一些子问题-每个子问题都由不同的对象处理。 如果Nabil,Julia和Alex正在制作太空侵略者游戏。 纳比尔(Nabil)可以建立一个代表外星人的对象,朱莉娅(Julia)可以建立一个GameBoard对象,该对象可以跟踪我的得分,生命数以及游戏开始/结束的时间,而亚历克斯(Alex)可以创建代表我的坦克及其导弹的对象。 最后,我们将这些对象组合在一起,以构建功能强大的游戏。 更新代码:使用模块化代码,随着时间的推移,我们可以轻松编写添加,更新或删除特定组件的代码。 例如,在用砖建造的建筑物中,您可以用新砖替换掉破裂的砖,而不必替换建筑物的整个部分。 与旧同在 虽然非常方便,但是OOP附带了很多问题: Massive View Controller,“ GOD”对象 :为了保持模块化,程序员使用一种类别,一种目的的主体。 但是,view肿的viewController和模型是流行的快速设计模式(MVC,MVVM)的常见副产品。 传递大块的对象会减慢您的代码的速度,并在构建时使模块化变得困难。 如果一个对象拥有您所需的太多功能,那么您编写的新代码也将最终取决于该对象。 多重所有权:假设您和一位工程师(Nick)正在使用引用同一BankAccount对象的单独ViewController。 如果您在firstViewController中添加功劳,而Nick在secondViewController中删除功劳,则两个人最终都会得到意外的结果。 类是通过引用传递的,因此,在项目的一个区域中更改值将导致该对象的值在所有其他位置均发生更改。 这使得很难测试功能对给定对象的隔离效果。 子类问题:如此之大的问题应有其自己的部分

鸡问题(面向协议的编程)

大家好 在本文上,我们来讨论协议扩展和面向协议的编程(POP)。 关于这个主题有很多讨论,Swift协议扩展和多重继承之间有很多比较。 因此,让我们讨论两者之间的区别以及POP听起来为什么更可靠的原因。 鸡问题 我喜欢用这个例子来说明一个小问题如何变成大规模开发和实际应用。 首先让我们看一下问题,然后再讨论。 想象一下我们需要构造鸟类的“对象/实体”的应用程序。 乍一看,我们将拥有Perrots,Sparrows和Falcons。 现在,让我们为此创建一个漂亮而智能的代码。 OOP和多重继承 所有这些鸟类共享许多东西,例如“喙”,“羽毛”,并且它们都可以“飞行”。 因此,让我们考虑以下Birds App的体系结构。 看起来很棒! 在这一点上,我们作为开发人员对我们的创作感到非常自豪。 它干净,可靠,我们只编写了一次代码,只是“ fly”方法的一种实现,这是一种非常复杂的算法。 现在,我们制作了该产品的版本1 。 此时,客户(Bird应用程序的所有者)说:“嘿,您知道吗,我注意到我的客户也希望拥有Chicken,所以请向此应用程序中添加Chickens,一旦您将它变得非常简单告诉我您有一个非常灵活的体系结构与OOP一起使用” 好的,这很容易……鸡是鸟,所以它将成为鸟的一个子类……但是等等……鸡不能飞! 因此,在这一点上,我是否需要重写此“ fly()”方法,并从“ Bird”中取消我所有漂亮的算法? 等等,这样做不仅是在重写代码,但是如果我心爱的客户决定明天他将发明一种新型的能飞的超级鸡肉,我该怎么办? 将“鸡”否定为“ fly()”的新“鸡”子类? 如果我的客户决定将应用程序移至南极并创建企鹅? 企鹅没有羽毛! OMG…我刚刚意识到我的整个体系结构对我的客户来说不够灵活! 我应该改变它吗? 我应该重写我的整个应用程序吗? 提出V2? 我应该更换我的客户吗? 我是否应该换一个鸟类完美的星球,它们都具有相同的特征? 好吧好吧让我们冷静一下 让我们尝试考虑可能的解决方案,毕竟,OOP很棒,并且确实可以反映现实世界的复杂性。 好吧,不完全是。 我们可以尝试使用OOP解决方案来奋斗。 我不会深入探讨我们可以尝试的失败解决方案: 创建两个基类:“鸟”和“飞鸟”(失败:如果客户决定购买飞机怎么办?) 使用协议/接口定义飞行方法(失败:重写很多代码) 将“ fly()”定义为接收可以飞行的对象的静态方法(失败:如果我有数百万个飞行对象怎么办?并发,多线程) 我与数百名不同的开发人员进行了此练习,让他们尝试许多解决方案。 简而言之,在OOP中解决此问题的唯一可能解决方案是使用多重继承,但是众所周知,这很危险。 只有很少的编程语言允许这种功能,并且在其自身的实现中有许多警告。 Pearl和Python使用一个有序列表进行多重继承,Java 8尝试使用编译器来避免错误,C ++实际上是真正在其完整扩展中真正实现多重继承的唯一语言之一。 因此,如果您不想仅在应用程序中创建C ++模块来解决该问题,就让我们现在尝试使用Swift功能来解决Chicken Chicken问题。 Swift […]

Swift中的模块化协议

Kecelakaan kerja yang saya alami ini bukan rekayasa。 ar! sesaat setelah kejadian itu warna merah ada dimana-mana。 saya震惊! Mereka membanjiri xcode sebagai tanda错误。 Begini kronologinya。 Tiket dan的最后期限是tak kenal kompromi的datang silih berganti。 Akhirnya demi kecepatan开发,saya menggunakan SJProgressHUD untuk merender tampilan加载。 Dengan membabibuta,SJProgressHUD dipasang di banyak ViewController。 Prinsip pertama yang saya langgar: 不要重复你的自我 ! 塞莱赛项目。 Selanjutnya Tinggal维护Saja。 […]

Swift:使用协议将自定义行为添加到UITableViewCell

开箱即用的UITableViewCells相当有限。 少数几种标准样式提供的标签和图像视图不多。 如果您希望单元格包含自定义按钮和控件怎么办? 充其量,您可以挂钩到详细信息显示按钮,并且可能会覆盖它的外观,但这有点不合常规。 通常最好建立自己的子类并使用自己的子视图和按钮设计表视图。 这是一个好的开始。 现在,我们希望控制器挂接到这两个按钮上。 您可能很想在表视图数据源的单元格配置步骤中向每个按钮添加自定义目标操作对。 这很危险,原因有两个: 如果不小心,可能会在重用单元格时意外地多次添加目标操作对。 捕获单击了其按钮的单元格的索引路径并不容易(遍历视图层次结构以达到单元格本身有点草率,并且UIView的tag属性对于复杂的表视图不是理想的选择) 快速方法:协议 Apple将Swift描述为一种面向协议的语言,因此让我们尝试一种面向协议的解决方案! 为您的单元格类创建一个自定义的委托样式协议。 我叫我SwiftyTableViewCellDelegate。 对于委托样式协议,您希望委托的用户对委托对象具有弱引用。 由于弱引用只能在类上使用,因此我们的协议将必须声明“类”一致性。 为您要跟踪的每个操作添加一个委托方法。 在我的示例中,我有一个心形按钮和一个垃圾按钮。 我想知道何时点击这些按钮中的任何一个,所以我为它们中的每个创建了一个委托函数。 将委托属性添加到您的自定义单元格类。 使它变弱和可选。 然后,将按钮操作连接到单元格中的方法,以调用这些委托方法。 这可以在情节提要中完成。 整合我们的自定义代表 使您的UITableViewController符合您刚刚设置的协议。 另外,还要确保将单元的委托设置为控制器本身。 最终结果看上去与我们最初的基于目标动作的方法并没有太大不同,但是它解决了我们两个主要问题! 由于单元格只有一个委托,因此每次使用该单元格时,表视图控制器都可以将自己分配为该单元格的委托,并且没有过度监听的风险。 由于委托函数在sender参数中发送单元格本身,因此我们可以要求表视图非常有效地查找索引路径。 精彩!

使用迭代器协议的斐波那契序列

Swift中的迭代器协议是任何序列的for-in循环背后的驱动力。 确认序列协议的任何类型(例如数组)都应提供自己的迭代器。 因此,斐波那契数列的实现看起来像这样 类别FibonacciSequence:Sequence { func makeIterator()-> FibonacciIterator { FibonacciIterator() } } 迭代器跟踪其迭代过程,并在序列中前进时一次返回一个元素。 迭代器必须实现next()函数,该函数返回序列的下一个元素。 struct FibonacciIterator:IteratorProtocol { var(firstNumber,secondNumber)=(0,1) 变异func next()->整数? { (firstNumber,secondNumber)= (secondNumber,firstNumber + secondNumber) 返回firstNumber } } next()函数计算斐波那契数列中的下一步或下一数字。 现在我们可以在for-in循环中使用FibonacciSequence 让fibonacciSequence = FibonacciSequence()。prefix(5) 用于fibonacciSequence { 打印(数量) } //打印1 1 2 3 5 注意,FibonacciIterator是无限迭代器,没有终止条件。 可以使用条件(例如序列的最大长度)或任何其他终止条件来终止序列。 例如,我们可以使用以下代码生成最多50个斐波那契数 let fibonacciSequence = FibonacciSequence()。prefix {$ 0 <50} 如果要实现迭代器,并希望在迭代器级别控制序列的终止,请添加终止条件 struct FibonacciIterator:IteratorProtocol […]

开始进行Swift编程第9部分-基本协议,扩展和下标

在上一篇文章中,我们介绍了类型转换,安全展开可选内容和访问控件。 Swift编程第8部分开始-类型转换,安全解压缩可选项和访问控制 在上一篇文章中,我们讨论了初始化,反初始化,覆盖和引用计数。 medium.com 没什么困难,对于您中的某些人而言,也许最好不过是适度的,因为有了访问控制的概念。 这篇文章可能会使事情再次升温,但这又仅仅是因为实际的应用程序用例。 我将尽力以一种可以帮助您理解所有内容以及何时应在何处使用所涉及的主题的方式来解释事情。 我知道有些教程可以直接跳转并提供扩展,但是我不会。 扩展是利用您已经知道的内容做更多事情的基础,因此,我决定最好从协议开始。 因此,让我们深入。 好的,所以协议和扩展声音令人生畏,Apple提供的定义在您开始时并不能消除浑水。 这个定义直接来自苹果的书: 协议定义了适合特定任务或功能的方法,属性和其他要求的蓝图。 然后,该协议可以由类,结构或枚举采用,以提供这些要求的实际实现。 满足协议要求的任何类型都被称为符合该协议。 你们中有些人可能已经了解到,其他人可能仍然会挠头。 不用担心,现在,让我们暂时忘记该定义,我有一个使用棒球的指导来指导我们进行定义。 在棒球中,我们有不同的位置,例如击球手,投手,接球手,垒手,外野手,教练和裁判员。 如果您对棒球有任何了解,您就会知道团队中的每个人都有自己的角色,每个人都擅长于该角色。 我们不能只是让三垒手成为投手,或者让裁判成为外野手。 第一种情况很难,因为团队喜欢为一垒手雇用左撇子,为三垒手雇用右撇子。 另一个似乎不是一个好主意,因为两队之间的裁判是中立的。 如果每个玩家都是一堂课, 那么他们如何玩游戏就由协议定义。 让我们看看它在代码中的外观。 扩展以简单的概念而得名。 扩展实质上是将功能扩展到类,结构或协议。 关于它们的使用方式,我将引用Swift中的Foundation库。 Foundation有一个名为UITableView的类。 UITableView本身仅处理显示表视图。 但是,我们可以通过给它一个名为UITableViewDataSource的扩展来扩展其功能。 这为采用此协议的类提供了提供数据如何在表视图内部显示的上下文的能力。 上图是扩展的一个很好的例子。 通过单击“无选择”,您可以打开当前布局的树。 这使您可以通过单击树的任何部分来浏览代码,Xcode将专注于该代码。 通过使用//MARK: — Properties ,可以在属性上方创建一条水平线,同样可以使用 //MARK: Properties —在属性下方放置一条水平线。 或者,您可以在上方和下方都设置一个,但是我通常只在上方放置一个,以将其分成这样的部分。 您可以选择最有意义的选项。 有关更多信息,请查看Apple的标记文档。 好的,只是我想离开这里的提示,回到扩展。 除了将您的代码分成逻辑区域外,它们还做了其他一些事情,这也成为我成为协议狂热者的原因,并尽可能尝试使用它们。 因此,协议对于团队来说很棒,从一开始就显而易见,但是您可能会问自己,如果您是一个单独的开发人员,为什么还要使用协议? 您知道一切都会做什么,并且不介意在一年后回到您的代码以发布更新时重新学习。 特别是如果这意味着必须编写更多的代码,这对于您的应用正常运行并不重要。 还记得Fielder协议吗? 如果我告诉您不必编写协议要求,也不必单击红色的停止符号以使它在每次使用协议的情况下都实现每个方法存根,该怎么办。 那您想要协议吗? 纯粹的事实是,在上面的示例中,如果您在同一实现中使用darn编写7次6个属性和4个方法,那将是疯狂的,那就是42个属性和28个方法,更不用说每种方法所涉及的逻辑了。 对我来说听起来像是折磨。 […]

创建iOS Viper模块-分步指南

什么是VIPER? 什么是VIPER模块? 模块应如何组装? 背景故事 最近,作为iOS开发团队,我们决定将项目代码库从Objective-C迁移到Swift,我们还讨论了使用新体系结构而不是将所有内容保留在MVC中的问题。 因此,打开XCode,选择文件和新项目,选择swift作为开发语言并重新开始,对吗? 🤔 不,有时候事情并不是那么容易。 实际上,如果整个团队都保留即将推出的新功能以及sprint目标以进行迁移过程,则产品经理将不会感到高兴。 因此,我们必须同时进行快速迁移和常见的sprint作业,才能使所有人满意。 Apple称之为Mix and Match ,可以从下面的链接查看详细信息, 将Swift与Cocoa和Objective-C结合使用(Swift 4.1):同一项目中的Swift和Objective-C 描述Swift与Objective-C语言和Cocoa / Cocoa Touch框架的兼容性的各个方面。 developer.apple.com 然而,这个故事与快速迁移无关,也有很多更好的文章。 如果我们专注于更改应用程序体系结构的决定,那么背后的原因是什么?实际上,我们一直在从事的iOS项目就像一个巨大的意大利面条,每个开发人员都会为每项新任务在配方中添加自己的调味料。 那么,好的架构应该提供哪些功能? 易于跟踪的数据流。 每个对象都有明确的角色,彼此分开。 易于测试。 事实是,我们的iOS项目显然缺少这些功能。 那么,什么迹象表明应用程序架构存在问题? 具有数千行代码的大规模视图控制器。 调试时迷路。 整个应用程序中越来越多的全局变量。 几乎无法测试。 毫不奇怪,这些迹象正在定义我们的项目。 因此,我们开始考虑其优缺点,开始搜索iOS中使用的不同架构模式。 经过大量阅读后,我们召开了会议,相互讨论并做出最终决定。 在会议结束时,每个人似乎都对名为VIPER的体系结构感到满意,这是我们的下一个体系结构。 什么是VIPER? Viper是一种将功能实现划分为一个模块的五个不同层的体系结构。 VIPER的每个字符代表一个不同的层。 V:检视 I:交互器 主持人:主持人 E:实体 R:路由器 每一层在模块中都有不同的作用。 在VIPER中,我认为要实现一个功能,例如从不同的部分构建乐高玩具。 让我们看一下每一层, 视图 视图是一个虚拟对象,大多数情况下会接收触摸事件。 代替MVC的具有数千行代码的大规模视图控制器,基本上所有与服务相关的代码和决策都不应存在于其中。 例如,当从用户接收到触摸事件时,视图对象应通知演示者,例如“我亲爱的演示者,已从用户接收到触摸,但我不知道该怎么办”。 主持人 我认为主持人是VIPER模块的核心。 模块中仅与所有其他层通信的层。 […]