Type System 是一项编程语言,或者说编译器所提供的便当。Pattern Matching 能够让咱们少写代码,而 Type System 能够让咱们少犯错误,缩小 Type 相干的各种 bug。
iOS 开发交换技术群:563513413,不论你是大牛还是小白都欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!
一般来说,咱们写代码时为了升高 bug 率,一是依赖于程序员本身的教训积攒,二是靠编译器做各种动态查看,type system 则是属于动态查看这一类。Swift 较之 Objective C 的 type system 有了很大的改良,上面文章中次要是介绍 Swift 相干的一些个性。在开始之前,先聊下如何靠程序员教训来升高 bug。
Bug
从我本身的体验推断,我置信大部分程序员在写代码的时候,对于代码是否存在 bug 是有肯定感知的。只不过有些新入行的敌人,在写代码的时候操之过急,或者因为和产品经理探讨吃了胜仗情绪不佳,coding 时指标变成了写能 work 的代码,而不是写高质量的代码。
facebook 面试有一个环节叫 whiteboard coding,要求程序员能在白板上写出简直是「bug free」的代码,这听起来有点骇人听闻,写代码没有 Xcode 提醒就罢了,bug free 更是难上加难了。写一段简直没有 bug 的代码到底有多难呢?说难不难,说易不易。
写代码时,慢一点,再慢一点。好好的想下代码有可能出错的中央在哪,想分明了 bug 就少。一般来说,要缩小 bug 量,一是靠程序员本身涵养,二是靠编译器提供的动态查看。Type System 属于第二类,在深刻之前,先简略聊下如何靠本身涵养升高 bug 率,晋升 bug 感知的第六感。
少写 Bug 的繁难准则
要晋升程序员的本身涵养来升高 bug 率,是个大话题,而且多和本身的常识积攒无关,须要长年累月的学习和养成。本文的目标不在于此,所以只介绍一个小技巧来养成感知 bug 的好习惯。
咱们能够粗略的将咱们所写代码分为 data 和 behavior,behavior 围绕 data 执行各种逻辑。一个函数能够看做是一个 behavior,而函数自身又由若干 data 和 behavior 所形成。很多时候,代码有 bug,是因为 data 呈现了意料之外的变动。有一个繁难准则能够缩小这类 bug:只有遇到 data,就做 aggressive check。
具体到一个自定义的函数,函数会蕴含哪些 data 呢?细心理一理没几个。
- 函数入参
- 外部长期变量
- 依赖的内部变量
- 返回的最终后果
这几类 data 是咱们在一个函数中最常常遇到的,只有咱们对他们做好查看就可保平安。做哪些查看呢?最常见的也就那么几样,比方是否为 0,为 nil,数组元素 count 为 0,如果期待负数则是否为正数,数组是否越界,多线程是否平安等。做下总结就能够实现大部分的可靠性查看。简而言之,只有是应用 data 的时候,就围绕 data 做好应该的查看,做到这点,写一个简直没有 bug 的函数就不怎么难了。
这个准则更精准的表白是:在任何场景下,无论是定义变量还是应用变量,都对变量的各种可能性做检查和爱护。
Type System
回到咱们的正题 Type System,Type System 是由编程语言和 type 相干的各种规定所形成。它的用途也简略,能够帮忙咱们缩小和 type 相干的 bug。
编程语言大多都有本人的 Type System,Objective C 和 Swift 都有。在开始探讨 Type System 之前,要明确 Type 的定义。
Type 就像自然语言里的名词,动词,介词等等,能够标准咱们的表白。在编程语言中,type 则是一种防止代码表白谬误的束缚。Type 不仅仅包含诸如 int,float,bool 这类 primitive type,对象的 class type,还包含 function,block 等不那么显著的 type。变量,常量,函数等等都(且肯定)具备 type 信息,有些一眼能看出,有些要靠推断。
Static vs Dynamic
有些 type 信息是交由程序员去推断和保护的,有些则是留给编译器去治理的。前者的 type 束缚是在 runtime 查看的,偏差「dynamic」,后者则在 compile 的时候就做了 check,偏差「static」。
很多技术文章都会探讨编程语言的 dynamic 和 static 属性,咱们要分分明 dynamic 和 static 其实是个宽泛的说法,他们可能蕴含不同的语义和场景。dynamic 和 static 既能够用来探讨 type system,又能够用来形容函数调用机制。比方咱们认为 Swift 是 statically typed,但 Objective C 的 runtime 和 message 机制又显然是 dynamic 的,这两种场景下 static 和 dynamic 说的其实不是一回事。
回到 type system 的场景,探讨下语言是 statically typed 还是 dynamically typed。还是要进一步看场景,在 Objective C 中,type 信息既能够是 static 的,也能够是 dynamic 的,看咱们如何应用了,比方上面的代码中 type 信息是 static 的:
int i = 0;
i = @“test”;
因为 type 的上下文信息是残缺的,编译器能够做类型判断。而如下代码中 type 信息则是 dynamic 的:
id obj = [NSData new];
obj = [NSObject new];
因为 id 能够指向任意对象类型,id 能够在不同的工夫点里指向不同的类型,编译器此时无奈依据类型信息作出判断,是否存在类型应用谬误的。所以咱们会说像 Objective C 这类编程语言在 type system 上,是同时具备 static 和 dynamic 属性的,要害还是看具体的应用场景。
但 Swift 却是货真价实的,纯正的 statically typed 编程语言,不具备任何 dynamically typed 的属性。比方在 Swift 中,如下代码是无奈通过编译的:
var i
编译器会提醒:Type annotation missing in pattern,也就是短少 type 信息。要申明一个变量,咱们能够通过如下两种形式来提供 type 信息:
Ios 代码
- var i = 0 // 形式一,implicit typing
- var i: Int // 形式二,explicit typing
形式一是通过赋值来做 type inference,形式二是通过显式的提供 type 信息。Swift 在 type 的应用上十分刻薄,当之无愧为 statically typed。
显然,static type 比 dynamic type 更平安,编译器能够帮咱们做类型查看,这也是为什么 Swift 比 Objective C 在 type safety 上更优良的起因。当然,dynamic type 并非全无益处,初期开发起来速度会快于 static type,而且省去了编译时的 type 查看,每次编译速度更快。毛病是一旦呈现 runtime 中的类型谬误,要花更多的工夫去调试,要写更多的 test case,筹备更多的文档。这种缺点在较大规模的我的项目上会更显著,Swift 抉择 static type 策略应该也有这方面的思考。
Type Inference
类型推断(type inference)也是 type system 当中的一个常见概念。不少编程语言比方 Swift 都有 type inference 的性能。type inference 有什么用途呢?statically typed 的编程语言决定了变量都必须具备类型信息,意味着咱们每次应用变量的时候都须要显式的申明 type 信息,比方在 Objective C 中,这样会显得有些繁琐和啰嗦,一旦有了 type inference,咱们能够在代码中省略掉很多对于 type 累赘的表述。咱们看如下代码:
Ios 代码
- var i = 0
这行代码中,有两个实体有 type 信息,变量 i 和常量 0,0 默认的 type 信息是 int,i 的 type 信息没有显示的申明进去,但在 Swift 中,因为 0 被赋值给了 i,所以能够通过 type inference 推断出 i 的 type 信息也是 int。这种类型推断会产生在很多程序员意识不到的角落,这种具备传染个性的 type 信息能够层层叠叠,一级一级的输送到更多的其余变量实体。编译器就是通过这种传染的个性来做 type inference 的。
在 Swift 中,type inference 配合 static type 让代码既精炼又平安。
Optional Type
后面提到 type 信息实质上是一种束缚,能够防止 type 的应用谬误。咱们在编写代码时,常常遇到的一种 bug 是对于空对象或者说对象为 nil 状况,漏写了为空的判断。Swift 通过引入 optional type 来强制开发者思考 nil 的场景,更妙的是,当「是否为 nil」成为 type 信息之后,编译器也能够一起来帮忙查看 nil 的应用场景。看下 optional type 的定义就一清二楚了:
Ios 代码
- public enum Optional<Wrapped> {
- case none
- case some(Wrapped)
- }
通过 enum type 来定义 optional type,以表白是否为 nil 的含意。这也是 Swift 为什么要引入 optional type 的根本原因,让编译器以类型查看的形式,来帮忙开发者剖析是否存在漏判 nil 的场景。
Generic Type
再次强调下,type 实质上是一种束缚。当咱们定义 int i 时,int 就成为了变量 i 的一种束缚。咱们能够把这种束缚进一步强化,比方引入 generic type(泛型)。generic type 有两个次要个性,其一是容许开发者在前期再指定 type 的值,其二是能够把 type 束缚施加到指定的代码范畴里。了解这两个个性,是咱们把握 generic type 各种表现形式的根底。
generic type 能够让咱们写出更加合乎 type safety 的代码,Objective C 和 Swift 都反对定义 generic type,只不过 Swift 中 generic 的概念更加宽泛,利用面也大很多,在一些显式的和隐式的中央都存在 generic 的身影。比方后面提到的 optional,其实也是个 generic type。
generic 能够作用于很多其余的简单 type,比方 optional 就是 generic 作用于 enum 的后果,除了 enum 之外,还有 struct,class,function 等都能够和 generic 搭配应用。咱们再看一个 Swift 自带的例子,Array:
Swift 代码
- public struct Array<Element> : RandomAccessCollection, MutableCollection {
- …
- public mutating func popLast() -> Element?
- …
- }
只须要在 struct 名字前面以 的模式,就能够在 struct 的作用域外部申明一个新的 xxx type(xxx 在 Array 的 extension 中也是可见的),xxx 能够在应用时再确定具体指代什么 type。应用 Array 的时候,咱们也不用显示的指明 xxx 代表什么,能够依赖后面提到的 type inference:
Ios 代码
- var arr = [Date()]
- arr.append(UILabel()) //compile error
下面第二行会报错,这是 Swift 和 Objective C 的差别之处,在 Objective C 中,咱们能够在 Array 中放入不同类型的对象,而在 Swift 中,一旦 Array 中的元素类型被 type inference 确定,就不能放入其余类型的对象了。generic type 和 type inference 配合的场景在 Swift 当中经常出现。
Named Type vs Compound Type
named type 指的是咱们传统意义上所了解的 data type,例如 int,float,string,自定义的 class 等等。在 Swift 中,设计者引入了 compound type 的概念,能够把 compound type 了解成 named type 的某种汇合,比方 function 和 tuple,他们往往都蕴含多个 named type。
咱们晓得在 Swift 中,function 是一等公民,能够作为变量申明,参数,返回值等等,要了解并使用这一点,须要在思维上做转换,把 function 也看做一种 data type(compound type),在原先应用 named type 的地位,咱们简直都能够应用 compound type。
compound type 加强了语言的表达力,但其灵活性在一些场景下,也会肯定水平的升高代码的可浏览性。compound type 能够包其余 compound type,能够一层层的套嵌,这种 nested compound type 有时候会让代码看上去没那么直观,比方上面一段 Swift 代码:
Swift 代码
- func someFunc(f: (Int)->(Int, ()->(Int))) {
- }
下面的函数里,function 和 tuple 作为 compound type 存在套嵌,代码自身尽管不长,要一眼把其中蕴含的 type 都辨认进去不那么容易。compound type 的应用可能是不少从 Objective C 转向 Swift 的同学初期感觉难以适应的起因之一。
总结
上述所提到的概念都是和 type system 相干的基础知识,尽管根底,却非常重要。对 type system 建设残缺全面的意识,多利用语言自身的 type 制约来防止 bug,能够让咱们对本人代码的安全性有更好的把握,对于代码品质的晋升也有极大的帮忙。
来自: MrPeak 杂货铺