Tag: Tiny系列

TinySolution:解决枚举的限制

此文章同时提供中文版本: TinySolution:解决enum的限制 Swift的enum比Objective-C强大得多。 我们可以基于具有不同参数的各种类型创建案例值,这在不同状态或对象具有不同数据类型的情况下很有用。 例如, Result类型非常有用。 顺便说一句: Result类型将在Swift5中正式支持。 ( SE-0235 ) 尽管enum功能强大,但仍有一些限制。 例如,我们经常需要绘制两条具有自定义线宽的实线和虚线。 目前,我们可以使用enum来实现。 在实际项目中,事情从未如此简单 除线宽外,虚线还需要支持自定义的虚线宽度和空格。 当然,我们可以将Line更改为以下格式: 问题已解决,但是大多数虚线使用相同的虚线宽度和空格。 我们不想每次都手动传递相同的值,因此我们尝试使用默认参数: 不幸的是,编译器告诉我们enum不支持默认参数。 但是我们在Objective-C上长期存在这种限制,并且我们熟悉如何以其他方式实现默认参数。 所以我们改为: 编译器告诉我们一切正常。 三个case对象的创建也成功。 那么问题解决了吗? 至少看起来是这样。 怪现象 当您开始编写switch方法时,事情并没有按您预期的那样工作。 例如,我添加了output()方法,并出现警告: 似乎前一个case dash(…)包含后一个情况的所有情况。 考虑到这一点,让我们交换一下: 但是,仍然出现相同的警告。 似乎这两种case dash(…)与编译器完全相同,因此我们无法找到确切的case 。 如果这是所有问题,那么这种实现在某些情况下仍然有用。 不幸的是,事实并非如此。 让我们根据Line的最后一个实现编写一些测试: 您可以看到Line.dash(width:1)被判断为default ,并且不属于Line.dash 。 因此,这种工具已经是一个例外。 后一种case将覆盖前一种情况。 前者只能创建而不能由switch判断,因此基本上这种case是没有用的。 也许这是一个Swift错误? 请告诉我你是否知道。

TinyExperience:何时使用内联函数

此文章同时提供中文版本: TinyExperience:内联函数的使用场景 我相信所有使用Swift的人都知道Swift中的功能。 但是内联函数对于大多数开发人员而言可能并不常见。 本文旨在介绍我在日常开发中使用内联函数的方式。 什么是内联函数 我们通常在类,结构,枚举等级别使用函数: 类示例{ func method1(){} } 内联函数是另一个函数内部的函数,如下所示: 类示例{ func method1(){ func inlineMethod1(){} } } 实际上,换句话说,内联函数只是函数的正常用法。 Objective-C不支持嵌套功能,Swift可以修复该功能,甚至不支持多层嵌套。 但是我们要注意的一件事是,内联函数属于本地声明,因此在使用它之前需要对其进行定义。 常用用法 由于我们不需要在Objective-C中使用它,为什么我们会在Swift中使用它呢? 例如,我们经常这样编写视图的初始化代码: loadView包含许多不同的初始化代码。 编写时可能很清楚,但是稍后查看时会非常混乱。 当然,写评论也是一个清楚的好方法,但是我们写评论的方式也不尽相同。 而且注释与内容没有直接关联,因此,在很多情况下注释无法跟上代码的进度。 更好的方法是使代码描述自己,就像提取到函数中一样,例如: 这应该是我们最常用的方法。 提取的函数使代码更易于理解和处理。 但是有一个问题:那些暴露的initxxx()方法可以从类内部的任何地方直接调用。 尽管我们知道它们只是初始化代码,只应从loadView调用一次,但是代码没有任何限制,您无法控制团队中的其他开发人员。 此时,我们可以使用内联函数解决此问题: 用内联函数替换三个initxxx()方法可以保留代码的结构,同时避免可能的错误调用,并且由于代码更加紧凑且不会分散在本地,因此有助于后期重构。 减少重复代码 减少重复代码始终是该功能的责任。 但是在过去使用Objective-C(仅适用于类级别的函数)时,我们通常仅将通用的重复代码提取到函数中。 一些部分重用的代码我们只是直接重复它们,因为如果所有本地逻辑提取到函数中,都会使我们的类级函数太混乱。 就像下面的示例一样, label.text具有多个重复设置。 该示例可能非常简单,但是我们仍然需要保持两组字符串之间的一致性。 但是使用内联函数,我们可以更彻底地实现DRY原理,以便完全复用复杂的部分,如下所示: 实际上,我们还可以利用函数的其他功能(例如参数传递)来使代码更结构化: 回调优化 除了优化类级别的某些原始功能外,内联函数还可以用于优化某些回调处理问题。 例如,我们经常需要发出网络请求,就会有多个回调,例如以下示例: 但是,当这样请求参数数量时,整个方法调用将非常长且不清楚。 我们可以使用换行符来处理它,但是这次您必须忍受Xcode怪异的缩进逻辑: 参数足够清晰,但是回调内容的缩进太多, }的缩进位置对我来说是不可接受的。 目前,我们可以使用内联函数来解决此问题,在添加两个内联函数之后,我们可以使代码更加清晰: 实际上,您可以像以前一样将onSuccess和onFailure方法提取到类层,但是使用内联函数的优点是这两个方法对其他方法不可用,这等效于限制了这些方法的范围。方法很好。 整个代码的结构将整洁,缩进程度低。 […]

TinyExtension:增强固定的URL初始化

此文章同时提供中文版本: TinyExtension:优化固定URL创建 我相信当今大多数应用程序都使用网络请求,因为网络使一切变为可能。 因此, URL类应该是每个人都熟悉的。 我相信每个人都会有直接用固定字符串创建URL的经验。 毕竟,第一个请求的url地址必须直接写在代码中。 因此,我们可以像这样在我们的应用中声明第一个网络请求地址: 让媒介= URL(字符串:“ https://medium.com/@GalvinLi”) 很好,但是使用起来可能很不方便,因为URL(string:)方法返回URL? ,并且每次使用时都需要将其拆开。 URL(string:)设计为返回Optional对象,因为参数字符串不一定是有效地址,但是我们可以保证代码中固定的地址中格式的正确性,因此可以强制展开: 让medium = URL(字符串:“ https://medium.com/@GalvinLi”)! 强制拆包具有一个特征,即如果直接使用它,它就像展开的对象一样工作。 但是,当您将其分配给另一个对象时,对象类型将更改回Optional对象。 因此,稳定的方法是直接将其声明为非Optional对象: 让媒介:URL = URL(字符串:“ https://medium.com/@GalvinLi”)! 现在,我们可以轻松使用此URL对象。 减少强制展开 当定义了许多URL对象时,将有很多强制展开。 在现实世界的项目中,作为一种良好的编程习惯,必须减少强制展开。 当我们习惯于对URL(string:)进行强制解包时,可以强制解开服务器返回的某些url地址,特别是对于经验不足的程序员而言。 为了解决这个问题,我们实际上可以通过一个简单的extension实现它: 此extension仅添加了一个返回URL的初始化方法。 但是这里传递的参数有些不同,不是常规String而是StaticString 。 这是Swift标准库提供的一种类型,某些人可能不知道,所以让我们看一下文档中的定义: 简而言之, StaticString定义了一个在编译时就已知的字符串,即在我们的代码中不接受任何变量的固定字符串。 这是区分需要手动处理的URL地址和代码中的固定URL的好方法。 使用它的方法非常简单: 让媒介= URL(staticString:“ https://medium.com/@GalvinLi”) 消除力解开可以使代码更干净,更安全。 更好的实施 StaticString使代码更安全,但我们可以使其更好。 对于编译器, “https://medium.com”是一个字符串,而URL()是一个url对象。 但是对于我们来说,以https开头的字符串已经可以表示url对象。 为了使编译器也知道这一点,我们需要添加另一个extension : ExpressibleByStringLiteral是用于初始化的protocol ,专门用于=初始化。 当然,这仍然是用于StaticString初始化而不是String ,现在我们可以使用=来直接初始化url,如下所示: 让媒介:URL […]

TinyExtension:简化的空闭包

此文章同时提供中文版本: TinyExtension:空封闭简化 在Swift中,我们经常使用Closure。 当然,大多数Closure用于实现特定的功能块,但是在某些情况下,我们将使用空Closure: 情况1:忽略回调 通常,将实现回调方法,但有时接口提供的功能比我们需要的更多。 例如,我们需要使用以下功能: func sampleRequest(success: (Bool, String) -> (), failure: () -> ()) { // some network request logic // with some local variable store logic } 通常,我们将实现success和failure回调,但是在某些情况下,我们将忽略某些回调。 例如,在预加载数据时,我们不需要处理failure回调。 毕竟,预加载是在后台调用的,错误消息永远不会响应UI,因此可以删除。 我们调用的代码如下: sampleRequest(success: { (isSuccess, text) in // do some complex thing }, failure: {}) 在这里,我们使用了空的Closure。 实际上,这很简单,因为failure回调不携带任何数据。 但是,如果我们的预加载在方法中完成,则也无需实现success回调,调用的效果将变为: sampleRequest(success: { _, _ […]