分享十条实用的Swift小提示

前言

虽然编程语言不会那么容易消逝,但坚持衰落范例的开发小组正在这么做。如果你正为移动设备开发应用程序,并且你还没有研究Swift,那么注意:当Swift涉及到Mac、iPhone、ipad、Apple Watch和未来设备的应用开发时,它不仅会排挤掉Objective-C,而且还会取代在Apple平台中做嵌入式开发的C语言。 Swift有大量有趣的语法、特性、特点,只要掌握了用法就可以利用好它们。

在这篇文章中我会带你浏览我选择出的10条小提示,并附有已验证的代码供大家试用。

1.类与协议的existential

Existential类型允许我们说出想要一个类型具有哪种功能,而不用请求某些特定的东西。比如我们可以写一个接收类或子类的函数:

func process(user: User) { }

之后我们写一个函数,让它能接收符合某个协议的任意类型对象:

func identify(thing: Identifiable) { }

Swift允许我们让existential同时代表类与协议

下例中,有一个协议和一个符合该协议的类

protocol CanCook { }
class CelebrityChef: CanCook { }

之后再有一个类,并附有一个子类

class Appliance { }
class Hairdryer: Appliance { }

现在我们有了一个定义东西是否CanCook的协议,和一个定义我们家里东西的类。当我们把这两个合二为一时候就变得复杂了——用餐饮工具(Appliance)做饭。

定义它们很简单,因为它们可以归入Appliance的子类,并符合CanCook

class Oven: Appliance, CanCook { }
class Microwave: Appliance, CanCook { }

Swift的existential可以支持使用它们。但除非你是认识某个大厨,不然你应该找不到一个大厨来你家做饭。类似的,除非你实在没办法,你也不会用一个吹风机做饭。

结果就是,这两个函数都不够好用——它们并没有完整描绘出我们想要接收的文件类型:

func makeDinner(using: Appliance) { }
func makeDinner(using: CanCook) { }

好在通过写Appliance & CanCook,Swift让我们能够把协议与子类合并到一个existential中。我们希望某些东西是日常工具(Appliance),并符合CanCook协议,就像这样:

func makeDinner(using: Appliance & CanCook) { }

2.协议扩展可以提供默认属性值

协议扩展为方法的执行提供了默认属性值,这些默认值之后可以被符合类型覆盖,但你也可以用它们为属性提供默认值。

下例中我们创建一个Fadeable协议,并在设定好的秒数后逐渐淡出:

protocol Fadeable {
 var fadeSpeed: TimeInterval { get }
 func fadeOut()
}

比起给所有符合类型添加各自的淡出速度和fadeOut()方法,我们可以在一个协议扩展中为它们提供默认值。

extension Fadeable where Self: UIView {
 var fadeSpeed: TimeInterval {
 return 1.0
 }

 func fadeOut() {
 UIView.animate(withDuration: fadeSpeed) {
  self.alpha = 0
 }
 }
}

这样你可以让新的子类符合它们,而不用担心重复写相同的默认值

class MyViewClass: UIView, Fadeable { }

3.检查所有的集合项目是否满足一个状态

Swift 4.2新推出了allSatisfy()方法,让它运行一个状态闭包(condition closure),如果传递给这个闭包后,所有元素都返回true,那么allSatisfy()就返回true

例如某人考试结果数组如下:

let scores = [85, 88, 95, 92]

根据一个学生是否所有考试都达到85分,决定他是否通过。

let passed = scores.allSatisfy { $0 >= 85 }

4.使用解构(destructuring)操作元祖(tuples)

解构能够把元祖分解成独立数值,这样就可以更容易的操作它们。比如你也许想调用这样一个函数:

func getCredentials() -> (name: String, password: String) {
 return ("Taylor Swift", "biebersux")
}

它会返回一个包含两个字符串的元祖,如果你想让他们继续在一起,你可以:

let user = getCredentials()
print(user.name)
print(user.password)

然而,重构让我们能够把它们分开:

let (username, password) = getCredentials()
print(username)
print(password)

你甚至可以在函数被调用完后做这些——它们是一样的:

let user = getCredentials()
let (username, password) = user

这个技术让Swift能够简单轻易地解决一个经典入门代码问题:怎样在不使用第三个变量的情况下,交换两个变量。

多亏重构,Swift才能有这种最简单的解决方式:

var a = 10
var b = 20
(a, b) = (b, a)

5.通过溢出(overflow)算符让加减法能够环绕处理

所有的Swift整型都有最大值,比如UInt8的最大值是255,Int64的最大值是9,223,372,036,854,775,807。

为了保证安全,如果超过整型的限值,Swift会自动崩溃。比如下面的代码在编译时没问题而运行时会崩溃

let highScore = Int8.max
let newHighScore = highScore + 1

因为它在Int8.max上加1,产生了超过Int8存储范围的128。尽管崩溃听起来不好,但是至少它保证了安全。

不过,Swift提供了另一种处理方法:我们可以用overflow做加法,它让Swift绕回最小值,而不是崩溃。

let highNumber = UInt8.max
let nextNumber = highNumber &+ 1

它实际上挺常用,例如MySQL数据库会自动分配整数ID到数据库表单的行中。但是当整数都用完后,它会绕回并从1开始查到未使用ID,其中有些会随时间被删除。

6.公众只读,个人可写

尽管Swift的访问控制过去倍受诟病,但通过使用2个不同的访问控制属性可以改善很多。

例如下面的结构代表一家银行:

struct Bank {
 var address: String
}

我们对address没有使用任何访问控制,意味着任何人都可以读取并改写它。如果我们对这个属性用private,别人是改不了它,但也无法读它了。

Swift做出了一个兼顾:public private(set) 它可以让一个属性可被读取,但不能被写入。这样所有人都可以读取我们银行的地址,但只有银行才能改它。

struct Bank {
 public private(set) var address: String
}

7.成员逐一初始化(memberwise initializers)与自定初始化协同

Swift结构默认用成员逐一初始化,它可以方便快捷地创建实例

struct Score {
 var player: String
 var score: Int
}

let highScore = Score(player: "twostraws", score: 556)

但是如果你创建自己的初始化,你会自动失去成员逐一初始化。这是考虑到安全问题:你的初始化似乎是做了一些你觉得很重要的额外工作,所以如果Swift还用成员逐一初始化,那你的额外工作会被跳过。

如果你想要你的初始化与成员逐一初始化同时使用,步骤很简单。把你的初始化声明到一个扩展中,像这样:

struct Score {
 var player: String
 var score: Int
}

extension Score {
 init(player: String) {
  self.player = player
  score = 0
 }
}

// 现在它们都可用了
let highScore1 = Score(player: "twostraws", score: 0)
let highScore2 = Score(player: "twostraws")

8.static vs class属性

Swift中的类属性可以用2种关键词创建:static 和 class。它们都能让一个类中所有实例共享某个属性,但static意味着final,即无法在子类中被覆盖。

例如我们可以创建一个Building类,并定义一个用于存储建筑规划的class属性,和一个用于存储安全须知的static属性。

class Building {
 class var zoningRestrictions: String {
  return "None"
 }

 static var safetyRequirements: [String] {
  return ["Fire escapes", "Sprinklers"]
 }
}

因为zoningRestrictions是class属性,可以在子类中修改,比如居民区建住房,商业区建写字楼等等。相对的safetyRequirements是一个static属性,意味着所有房屋和子类必须符合安全法规。

代码如下:

class Skyscraper: Building {
 // this is allowed
 override class var zoningRestrictions: String {
  return "Dense commercial only"
 }

 // but this is not
 override static var safetyRequirements: [String] {
  return ["Sprinklers"]
 }
}

9. == 和 === 是不一样的

==运算符用于检测两个Equatable类型是否相等,例如

1 == 1
"kayak" == String("kayak".reversed())
[2, 4, 6] == [1, 2, 3].map { $0 * 2 }

通过对Equatable的自动综合分析,对==的支持就像对类型定义添加Equatable一样简单。但如果是对类,有另一个运算符:===。

因为类中的实例只不过是对内存特定地址的引用,===用于检查一个类中的2个实例是否指向同一段内存地址。

所以下面的情况会被认为是true

class Lightsaber {
 var color = "Blue"
}

let saber1 = Lightsaber()
let saber2 = saber1
saber1 === saber2

===运算符完全不使用Equatable,这就是说如果你创建2个拥有相同属性的独立对象,===会返回false

let saber3 = Lightsaber()
saber1 === saber3

10.通过numericCast()在整型间转换

在使用整数方面,Swift一直有高度选择性,如果你不留意,经常会发现你的代码中分散着Int(), UInt32(),和其他类型转换。也许这段代码不会出错,但它并不易于阅读:这就是为什么我们需要强制制定一种整型。

Swift有个专用的整型转换函数numericCast() 用了它就可以做到“我不关心这里需要什么类型,请查明白”。这样比起硬编码的类型,它可以更清楚的传达你的意图:为了运行的更好,你需要把一种整型转换到另外一种,但并不关心到底是怎么转换的

它的常用地点之一是arc4random_uniform()函数,这个函数会接收一个UInt32参数并返回一个UInt32,这里经常要在Int与UInt32之间加类型转换。

使用numericCast的话,你就可以写出很好的任意范围的实现

func random(in range: Range<int>) -> Int {
 return numericCast(arc4random_uniform(numericCast(range.count)))
  + range.lowerBound
}</int>

额外小技巧:如果不用 ! 那用什么

不是所有人都喜欢NOT运算符,!,主要是因为它读起来不自然。然而Swift中功能,方法,闭包,运算符之间的界限变得模糊了。所以如果你想的话,可以把!转化为它的函数:

let not = (!)

现在你可以用not(someBool)代替!someBool

let loggedIn = false

if not(loggedIn) {
 print("Please log in.")
}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 在一个项目中同时使用Swift和Objective-C代码混合编程的方法

    Swift 与 Objective-C 的兼容能力使你可以在同一个工程中同时使用两种语言.你可以用这种叫做 mix and match 的特性来开发基于混合语言的应用,可以用 Swfit 的最新特性实现应用的一部分功能,并无缝地并入已有的 Objective-C 的代码中. Mix and Match 概述 Objective-C 和 Swift 文件可以在一个工程中并存,不管这个工程原本是基于 Objective-C 还是 Swift.你可以直接往现有工程中简单地添加另一种语言的源文件.这种自

  • Swift中的可变参数函数介绍

    可变参数函数指的是可以接受任意多个参数的函数,我们最熟悉的可能就是 NSString 的 -stringWithFormat:方法了.在 Objective-C 中,我们使用这个方法生成字符串的写法是这样的: 复制代码 代码如下: NSString *name = @"Tom"; NSDate *date = [NSDate date]; NSString *string = [NSString stringWithFormat:                 @"Hell

  • Swift中动态调用实例方法介绍

    在 Swift 中有一类很有意思的写法,可以让我们不直接使用实例来调用这个实例上的方法,而是通过类型取出这个类型的某个实例方法的签名,然后再通过传递实例来拿到实际需要调用的方法.比如我们有这样的定义: 复制代码 代码如下: class MyClass {     func method(number: Int) -> Int {         return number + 1     } } 想要调用 method 方法的话,最普通的使用方式是生成MyClass的实例,然后用.method来

  • Swift之UITabBarController 导航控制器的自定义

    swift导航控制器,导航控制器类继承UITabBarController,具体代码如下所示: // AppDelegate.swift // Housekeeper // // Created by 卢洋 on //. // Copyright © 年 奈文摩尔. All rights reserved. // import Foundation import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicat

  • Swift在控件中添加点击手势的方法

    今天有同行问我,如何在tableview的headerview中添加点击方法,今天就来简简单单说明一下,在swift中添加点击手势的方法是: 复制代码 代码如下: imagepath.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "imagePathClick:")) 怎么样,看上去是不是比oc简单多了呢,简单解释一下,imagePath是我定义的一个UIImageView,可能有人添加了这个手势之后

  • swift中的正则表达式小结

    作为一门先进的编程语言,Swift 可以说吸收了众多其他先进语言的优点,但是有一点却是让人略微失望的,就是 Swift 至今为止并没有在语言层面上支持正则表达式. 正则表达式的用处: 判断给定的字符串是否符合某一种规则(专门用于操作字符串) - 电话号码,电子邮箱,URL... - 可以直接百度别人写好的正则 - 别人真的写好了,而且测试过了,我们可以直接用 - 要写出没有漏洞正则判断,需要大量的测试,通常最终结果非常负责 过滤筛选字符串,网络爬虫 替换文字,QQ聊天,图文混排 语法规则 使用过

  • Swift流程控制之循环语句和判断语句详解

    Swift提供了所有c类语言的控制流结构.包括for和while循环来执行一个任务多次:if和switch语句来执行确定的条件下不同的分支的代码:break和continue关键字能将运行流程转到你代码的另一个点上. 除了C语言传统的for-condition-increment循环,Swift加入了for-in循环,能更加容易的遍历arrays, dictionaries, ranges, strings等其他序列类型. Swift的switch语句也比C语言的要强大很多. Swift中swi

  • 利用Swift如何判断iPhone X机型详解

    前言 Apple Special Event 已经结束,iPhone 三款新机型和技术细节也已完全暴露.可以说,发布会前泄漏了 95% 以上的信息,以致于发布会一点惊喜也没有. 尽管 iPhone 8 实际上只是 iPhone 7s,而 iPhone X 我们也早已对它非常熟悉--特别是刘海.无论是满意还是失望,作为 iOS 开发者的我,还是得积极的适配起这款面向未来的机型. 我将在一边适配的过程中,一边从技术角度更多地去理解这款机器. 奇点在 iPhone X 的 break 除非你的 App

  • Swift教程之枚举类型详解

    枚举定义了一个常用的具有相关性的一组数据,并在你的代码中以一个安全的方式使用它们. 如果你熟悉C语言,你就会知道,C语言中的枚举指定相关名称为一组整数值.在Swift中枚举更为灵活,不必为枚举的每个成员提供一个值.如果一个值(被称为"原始"的值)被提供给每个枚举成员,则该值可以是一个字符串,一个字符,或者任何整数或浮点类型的值. 另外,枚举成员可以指定任何类型,每个成员都可以存储的不同的相关值,就像其他语言中使用集合或变体.你还可以定义一组通用的相关成员为一个枚举,每一种都有不同的一组

  • Swift的74个常用内置函数介绍

    Swift包含了74个内置函数,但在 The Swift Programming Langage 一书中只介绍了其中的7个,其它的都没有在文档中体现. 这篇文章列举出了所有的Swift库函数.文中所谓的 内置函数 是指无需引入任何模块(比如说Fundation等)即可以直接使用的函数. 下面先来看看7个在文档中提到的库函数: 下面列出一些很实用,但未在文档中体现的库函数: 复制代码 代码如下: //断言,参数如果为`true`则继续,否则抛出异常 //assert mentioned on pa

随机推荐