学Swift速记

Property(属性)

属性将值与特定的类、结构体或枚举联系起来。Stored属性将常量或变量存储作为实例的一部分,而computed属性计算而不是存储值。Computed属性是类、结构体或枚举提供的,Stored属性只有类或结构体提供 。

Stored或Computed属性通常与特定类型的实例相关联。但是,属性也可以与类型本身相关联。这样的属性称为type property

最简单的情况下,stored property就是作为一个类、结构体的实例的一部分而存储。

必须将一个lazy属性声明为var,因为它的值可能在实例初始化完成后才能获得。constant要求在初始化完成时就已经有值,因此不能是lazy的。

Lazy属性在初始化依靠于外部因素时比较有用,如果一个属性的值要复杂计算开销很大时,使用lazy也是很合适的

注意,lazy属性的初始化过程不是线程安全的!

Objective C中提供了两个方式来存储值和引用,除了属性,你还可以使用instance variable as a backing store。
Swift将这些概念统一到了一个单独的属性声明中。一个Swift属性没有对应的instance variable,属性的backing store不能直接被访问。这种方式避免了值在不同上下文如何访问的困惑。属性的所有信息,都作为类型定义的一部分在同一个位置定义。

除了Stored属性,类、结构体和枚举还提供computed属性,它实际上不存储值,只是提供一个getter方法(setter方法可选)来间接获取和设置其它属性的值。

如果将一个有observer的属性作为函数的in-out参数传递,那么willSetdidSet总会被调用。因为in-out参数的copy-in、copy-out内存模型使得函数在结束时总会回写属性的值。

关于全局和局部变量

全局的变量或常量总是惰性计算的,类似于惰性存储的属性,不同的是全局的常量和变量不需要声明为lazy
局部常量和变量永远不会惰性计算

Type Property

Type属性必须给初值,因为没有initializer来初始化它。Stored type属性第一次访问是惰性的,它们能保证只初始化一次。

继承

Swift的类没有继承一个通用的基类。如果没有给定义的类指定superclass的话,那么你定义的这个类就是自己的基类。

Overriding

  1. 重写Property Getters 和 Setters
    可以通过提供自定义的getter或setter重写任何继承的属性,不管这个属性是stored还是computed属性。子类并不是知道继承属性是stored还是computed,它只知道属性的类型和名字。
    可以通过提供getter和setter来重写只读属性为可读写的属性。但是,不能将可读写的属性重写为只读的。

如果你提供了属性setter方法的重写,你也必须提供getter方法。如果你不想在getter方法内修改继承属性的值,只需要在方法内简单地返回super.someProperty

  1. 重写Property Observers

不能给继承的常量stored属性或者只读computed属性添加observers。这些属性的值是不能修改的
同时,你不能同时重写一个属性的setter和observer。如果你想观测属性值的改变,同时也想提供自定义的setter方法,你只需要在setter方法中观测值的改变就好了。

3.重写方法

禁止Overrides

可以通过设置method、property或subscript为final来禁止重写。可以将class标记为final,这样任何类都不能继承这个类。

Structure也支持class的很多行为,比如methods和initializer,最重要的一点区别就是structure传递的时候总是被拷贝的,但是class是通过引用。Structure适合一些轻量级的不需要继承和类型转换的数据结构。

Protocol

A protocol defines a blueprint of methods,properties and other requirements that suit a particular task or piece of functionality.

Protocol可以被class、structure和enumeration实现

protocol ExampleProtocol{
    var simpleDescription:String {get}
    func adjust()
}

{get}表示这个属性是只读的,不能被修改

Protocol are first-class types,which means they can be treated like other named types.即协议也跟普通的数据类型一样被对待。比如,也可以创建一个协议的数组等。

ARC

类间相互引用的三种情况

  1. 两个属性都可以是nil,互相引用时可能会导致强引用循环。这个场景最好用weak reference进行解决(人和住宅的例子)
  2. 两个属性,一个可以是nil,另一个不能为nil,这种场景下最好用unowned reference(人和信用卡的例子)
  3. 两个属性都必须不为nil时。这种情况下,一个类使用unowned reference,另一个使用implicit unwrapped optional。

Initialization

Classes和Structures必须在实例创建时给所有的stored属性赋初始值。可以在initializer中赋值,也可以在声明属性时设置默认值。

当设置stored属性默认值或在initializer中初始化时,属性的observer不会被调用

Initializer delegation:Initializer可以调用其他的initializer来完成初始化过程,避免重复的代码

Value types(structures和enumerations)不支持继承,所以initialize delegate只是调用自己定义的其他initializer。

如果你想同时用默认的default initializer和memberwise initializer、自定义的initializer来初始化,那么可以将自定义的initializer放到extension中。

Designated Initializer And Convenience Initializer

Designated Initializer是类的主要初始化方法,它完全初始化所有的属性,调用superclass的initializer来传递superclass chain。每个类都至少要有一个。
Convenience Initializer是次要的,支持性的initializer。可以定义它来调用designated initializer(设置某些属性为默认值)。

Initializer Delegation For Class Types

规则1
Designated initializer必须调用 immediate superclass的designated initializer
规则2
Convenience initializer必须调用同一个类的initializer
规则3
Convenience initializer必须调用designated initializer

Initializer.png

简单的方式,

  • Designated initializer必须delegate up
  • Convenience initializer必须delegate cross

Two-Phase Initialization

Swift的编译器为了确保two-phase initialization正确执行而进行一些安全性检查。
Safety Check1
Designated initializer必须确保在initializer传递到superclass前所有的属性都已经初始化了。
只要所有的stored属性都初始化,就认为这个对象的内存已经完全初始化了。所以,designated initializer必须确保在传递初始化链前所有的属性都初始化。
Safety Check2
Designated initializer必须在给继承属性赋值前调用superclass initializer,如果不这么做,赋值会被superclass的initializer覆盖。
Safety Check3
Convenience Initializer必须在给任何属性赋值前调用其他的initializer。如果不这么做,赋值会被designated initializer覆盖。
Safety Check4
Initializer不能调用任何实例方法,读取任何实例属性,或引用self,直到initializer的first phase完成。

Swift初始化主要分两步,第一步给每个stored属性赋一个初始值,一旦每个属性的初始值确定,第二个步骤就开始了,在实例被使用之前,每个类都有机会自定义stored属性。

Swift的两步初始化过程与OC的类似。不同点在于第一步时,OC给每个属性赋zero或null。Swift的初始化更灵活,可以有自定义的初始值。

Phase 1

  • Initializer被调用
  • 分配内存,但内存还未初始化
  • designated intializer确认所有的stored属性都有值,这些属性的内存开始初始化
  • designated initializer交给superclass的initializer给它自己的属性进行同样的任务
  • 继承链进行传递
  • 继承链到顶之后,链上的最后一个类确保所有的stored属性都有值,实例的内存被认为完全初始化了

Phase 2

  • 从继承链往回遍历,每个designated initializer都有机会自定义实例。initializer可以访问self,修改属性,调用实例方法等等
  • 最后,任何的convenience initializer可以选择自定义实例

Swift的subclass默认是不继承superclass的initializer的。但是特定情况下superclass的initializer会被继承。假设subclass引入的新的属性都有默认值,那么会应用下面规则:
规则1
如果subclass没有定义任何的initializer,它自动继承superclass所有的designated initializer。
规则2
如果subclass提供了所有的designated initializer的实现(通过规则1或自定义),那么它自动继承所有的convenience initializer。(以convenience的方式也可以)

不能定义相同参数和名称的failable、nonfailable initializer

failable initializer创建一个optional值。

如果使用closure来初始化属性,牢记在closure执行完毕之前其余的实例都没有被初始化。这意味着在closure中不能访问其他任意属性,即使它有默认值。也不能使用self属性,或调用实例方法。

Closure

Global和nested function其实是特殊形式的closure。Closure有三种形式:

  • Global function,有名称,不捕获任何值
  • Nested function,有名称,捕获包含它的function内的值
  • Closure,没名称,捕获上下文的值

Trailing Closure

如果closure作为function的最后一个参数,并且closure定义非常长,那么可以使用trailing closure的技巧,将closure定义写在函数的外面。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    // function定义
}
 
// here's how you call this function without using a trailing closure:
 
func someFunctionThatTakesAClosure({
    // closure定义
})
 
// here's how you call this function with a trailing closure instead:
 
func someFunctionThatTakesAClosure() {
    // trailing closure's 定义
}

Nonescaping Closures

当closure作为function的参数,但是在function返回之后才被调用,就说这个closure escape a function。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

##Autoclosures
Autoclose是一个包含表达式、作为函数参数的的closure。它不包含任何参数,当调用时,它返回表达式的值。Autoclosure能延迟evaluation。
//Autoclosure,此时closure不执行
let customerProvider = { customersInLine.removeAtIndex(0) }
print(customersInLine.count)
// Prints "5"

//此时closure被真正调用 
print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"
  • AnyObject可以表示任意类的实例
  • Any可以表示任何类型的实例,包括函数类型

Extensions

Extensions可以给现有的class、structure、enumeration或protocol添加新的功能。与OC中的category类似,但是没有名字。
Extensions可以:

  • 添加computed实例属性和类型属性
  • 定义实例方法和类方法
  • 提供新的initializer
  • 定义subscript
  • 定义和使用新的nexted types
  • 让现有类型服从某一协议
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容