10条Swift小提示

2018 年 6 月 23 日 CocoaChina

Swift有大量有趣的语法、特性、特点,只要掌握了用法就可以利用好它们。在这篇文章中我会带你浏览我选择出的10条小提示,并附有已验证的代码供大家试用。


1.类与协议的existential


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

func process(user: User) { }


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

func identify(thing: Identifiable) { }


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

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

protocol CanCook { }
class CelebrityChefCanCook { }


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

class Appliance { }
class HairdryerAppliance { }


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


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

class OvenApplianceCanCook { }
class MicrowaveApplianceCanCook { }


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 MyViewClassUIViewFadeable { }


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(setvar 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"score0)
let highScore2 = Score(player: "twostraws")


8. static vs class属性


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


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

class Building {
    class var zoningRestrictionsString {
        return "None"
    }

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


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

class SkyscraperBuilding {
    // this is allowed
    override class var zoningRestrictionsString {
        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
}


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


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

let not = (!)


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

let loggedIn = false

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


相关推荐:


登录查看更多
0

相关内容

苹果公司在 WWDC 2014 开幕 Keynote 上发布的全新编程语言,具有更多现代化特性,同时容易使用,定位是补充 Objective-C. > Swift is an innovative new programming language for Cocoa and Cocoa Touch. Writing code is interactive and fun, the syntax is concise yet expressive, and apps run lightning-fast. Swift is ready for your next iOS and OS X project — or for addition into your current app — because Swift code works side-by-side with Objective-C.

Swift - Apple Developer

专知会员服务
42+阅读 · 2020年7月7日
【实用书】学习用Python编写代码进行数据分析,103页pdf
专知会员服务
192+阅读 · 2020年6月29日
【实用书】Python技术手册,第三版767页pdf
专知会员服务
234+阅读 · 2020年5月21日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
224+阅读 · 2020年3月22日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
Python 3.8.0来了!
数据派THU
5+阅读 · 2019年10月22日
5大必知的图算法,附Python代码实现
AI100
4+阅读 · 2019年9月10日
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
Python 热图进阶
专知
15+阅读 · 2019年5月4日
已删除
将门创投
18+阅读 · 2019年2月18日
如何编写完美的 Python 命令行程序?
CSDN
5+阅读 · 2019年1月19日
一天精通无人中级篇:遥控器协议 S-BUS
无人机
51+阅读 · 2018年12月20日
实战 | 用Python做图像处理(二)
七月在线实验室
17+阅读 · 2018年5月25日
快乐的迁移到 Python3
Python程序员
5+阅读 · 2018年3月25日
ggstance:ggplot2的水平版本
R语言中文社区
5+阅读 · 2017年11月17日
Seeing What a GAN Cannot Generate
Arxiv
8+阅读 · 2019年10月24日
Meta-Learning with Implicit Gradients
Arxiv
13+阅读 · 2019年9月10日
Feature Selection Library (MATLAB Toolbox)
Arxiv
7+阅读 · 2018年8月6日
Arxiv
4+阅读 · 2018年4月10日
Arxiv
6+阅读 · 2018年2月7日
Arxiv
6+阅读 · 2016年1月15日
VIP会员
相关VIP内容
专知会员服务
42+阅读 · 2020年7月7日
【实用书】学习用Python编写代码进行数据分析,103页pdf
专知会员服务
192+阅读 · 2020年6月29日
【实用书】Python技术手册,第三版767页pdf
专知会员服务
234+阅读 · 2020年5月21日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
224+阅读 · 2020年3月22日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
【电子书】C++ Primer Plus 第6版,附PDF
专知会员服务
87+阅读 · 2019年11月25日
相关资讯
Python 3.8.0来了!
数据派THU
5+阅读 · 2019年10月22日
5大必知的图算法,附Python代码实现
AI100
4+阅读 · 2019年9月10日
渗透某德棋牌游戏
黑白之道
12+阅读 · 2019年5月17日
Python 热图进阶
专知
15+阅读 · 2019年5月4日
已删除
将门创投
18+阅读 · 2019年2月18日
如何编写完美的 Python 命令行程序?
CSDN
5+阅读 · 2019年1月19日
一天精通无人中级篇:遥控器协议 S-BUS
无人机
51+阅读 · 2018年12月20日
实战 | 用Python做图像处理(二)
七月在线实验室
17+阅读 · 2018年5月25日
快乐的迁移到 Python3
Python程序员
5+阅读 · 2018年3月25日
ggstance:ggplot2的水平版本
R语言中文社区
5+阅读 · 2017年11月17日
相关论文
Seeing What a GAN Cannot Generate
Arxiv
8+阅读 · 2019年10月24日
Meta-Learning with Implicit Gradients
Arxiv
13+阅读 · 2019年9月10日
Feature Selection Library (MATLAB Toolbox)
Arxiv
7+阅读 · 2018年8月6日
Arxiv
4+阅读 · 2018年4月10日
Arxiv
6+阅读 · 2018年2月7日
Arxiv
6+阅读 · 2016年1月15日
Top
微信扫码咨询专知VIP会员