在函数式编程的核心哲学里,函数就是值,函数和结构体、整数、多元组、或者类,并没有任何区别。
运算符 -- infix -- associativity left 表明这个运算满足左结合律
结构体和枚举是值类型。默认情况下,值类型的属性不能在它的实例方法中被修改。
mutating 关键字用来标记一个会修改结构体的方法。表示可以在该方法中修改它所属的实例以及实例的任意属性的值。
使用 defer 代码块来表示在函数返回前,函数中最后执行的代码。
最好使用 Int ,即使你要存储的值已知是非负的。统一使用 Int 可以提高代码的可复用性和一致性。
在 OC 中, nil 是一个指向不存在对象的指针。在 Swift 中, nil 不是指针——它是一个确定的值,用来表示值缺失。任何类型的可选状态都可以被设置为 nil ,不只是对象类型。
与 C 语言和 OC 不同,Swift 的赋值操作并不返回任何值。
Swift 也提供恒等( === )和不恒等( !== )这两个比较符来判断两个对象是否引用同一个对象实例。
空合运算符( a ?? b )将对可选类型 a 进行空判断,如果 a 包含一个值就进行解封,否则就返回一个默认值 b (表达式 a 必须是 Optional 类型。默认值 b 的类型必须要和 a 存储值的类型保持一致)
guard 的执行取决于一个表达式的布尔值。按需使用 guard 语句会提升我们代码的可读性。
如何理解闭包
1. 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。
2. 闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。
3. 在函数章节中介绍的全局和嵌套函数实际上也是特殊的闭包,闭包采取如下三种形式之一:
• 全局函数是一个有名字但不会捕获任何值的闭包
• 嵌套函数是一个有名字并可以捕获其封闭函数域内值的闭包
• 闭包表达式是一个利用轻量级语法所写的可以捕获其上下文中变量或常量值的匿名闭包
闭包的循环强引用
如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用。Swift 使用捕获列表来打破这种循环强引用。
自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。
逃逸闭包 -- 136
注意闭包结尾的大括号后面接了一对空的小括号。这用来告诉 Swift 立即执行此闭包。如果你忽略了这对括号,相当于将闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
枚举
与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的 CompassPoint 例子中, north , south , east 和 west 不会被隐式地赋值为 0 , 1 , 2 和 3 。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的 CompassPoint 类型。
原始值和关联值是不同的。原始值是在定义枚举时被预先填充的值。对于一个特定的枚举成员,它的原始值始终不变。关联值是创建一个基于枚举成员的常量或变量时才设置的值,枚举成员的关联值可以变化。
类和结构体对比 -- 引用类型和值类型
Swift 中类和结构体有很多共同点。共同处在于:
• 定义属性用于存储值
• 定义方法用于提供功能
• 定义下标操作使得可以通过下标语法来访问实例所包含的值
• 定义构造器用于生成初始化值
• 通过扩展以增加默认实现的功能
• 实现协议以提供某种标准功能
与结构体相比,类还有如下的附加功能:
• 继承允许一个类继承另一个类的特征
• 类型转换允许在运行时检查和解释一个类实例的类型
• 析构器允许一个类实例释放任何其所被分配的资源
• 引用计数允许对一个类的多次引用
结构体总是通过被复制的方式在代码中传递,不使用引用计数
1. Unlike structures, class instances do not receive a `default memberwise initializer`.
2. In Swift, many basic data types such as String, Array and Dictionary are implemented as `structures`.
3. This behavior is different from Foundation: NSString, NSArray and NSDictionary are implemented as `classes`, `not structures`.
4. `Computed properties` are provided by classes, structures, and enumerations.
`Stored properties` are provided only by classes and structures.
5. `Property observers` can be added to `stored properties` you define yourself.
6. This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties.
NOTE_1
1. You must declare computed properties—including read-only computed properties—as variable properties with the `var` keyword, because their value is not fixed.
The `let` keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.
2. You can add `property observers` to any stored properties you define, except for lazy stored properties.
3. You can present an `inherited read-only property` as a read-write property by providing both a getter and a setter in your subclass property override.
You cannot, however, present an inherited read-write property as a read-only property.
NOTE_2
1. If you provide a setter as part of a property override, you must also provide a getter for that override.
2. If you don’t want to modify the inherited property’s value within the overriding getter, you can simply pass through the inherited value by returning super.some Property from the getter, where some Property is the name of the property you are overriding.
=== -- 检测两个常量或者变量是否引用同一个实例
“等价于”表示两个类类型(class type)的常量或者变量引用同一个类实例
“等于”表示两个实例的值“相等”或“相同”,判定时要遵照设计者定义的评判标准,因此相对于“相等”来说,这是一种更加合适的叫法。
Swift 中,许多基本类型,诸如 String , Array 和 Dictionary 类型均以结构体的形式实现。
这意味着被赋值给新的常量或变量,或者被传入函数或方法中时,它们的值会被拷贝。
Objective-C 中 NSString , NSArray 和 NSDictionary 类型均以类的形式实现,而并非结构体。
它们在被赋值或者被传入函数或方法时,不会发生值拷贝,而是传递现有实例的引用。
这种行为是由于结构体(struct)属于值类型。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
属于引用类型的类(class)则不一样。把一个引用类型的实例赋给一个常量后,仍然可以修改该实例的变量属性。
如果一个被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。
类型属性
使用关键字 static 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 class 来支持子类对父类的实现进行重写。
枚举或者结构体中的类型方法,要以 static 声明修饰符标记,而对于类中的类型方法,除了使用 static ,还可使用 class 声明修饰符标记。
在一个类声明中,使用关键字 static 与同时使用 class 和 final 去标记一个声明的效果相同。
结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。
因为允许在调用 advance(to:) 时候忽略返回值,不会产生编译警告,所以函数被标注为 @ discardableResult 属性。
override -- 方法,属性或下标
• 指定构造器必须总是向上代理
• 便利构造器必须总是横向代理
两段式构造过程
安全检查 1 2 3 4
阶段 1 + 阶段 2
跟 Objective-C 中的子类不同,Swift 中的子类默认情况下不会继承父类的构造器。
弱引用 && 无主引用
Person 和 Apartment 的例子展示了两个属性的值都允许为 nil ,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
Customer 和 CreditCard 的例子展示了一个属性的值允许为 nil ,而另一个属性的值不允许为 nil ,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
第三种场景,两个属性都必须有值,并且初始化完成后永远不会为 nil 。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
闭包内的循环强引用
在闭包和捕获的实例总是互相引用并且总是同时销毁时,将闭包内的捕获定义为 无主引用 。
如果被捕获的引用绝对不会变为 nil ,应该用无主引用,而不是弱引用。
相反的,在被捕获的引用可能会变为 nil 时,将闭包内的捕获定义为 弱引用 。弱引用总是可选类型,
? -- ! == 它们的主要区别在于当可选值为空时可选链式调用只会调用失败,然而强制展开将会触发运行时错误。
类型转换在 Swift 中使用 is 和 as 操作符实现。
用类型检查操作符( is )来检查一个实例是否属于特定子类型。
• Any 可以表示任何类型,包括函数类型。
• AnyObject 可以表示任何类类型的实例。
Extension -- 扩展可以为已有类型添加属性、方法、下标以及构造器
扩展可以添加新的计算型属性,但是不可以添加存储型属性,也不可以为已有属性添加属性观察器
扩展能为类添加新的便利构造器,但是它们不能为类添加新的指定构造器或析构器。指定构造器和析构器必须总是由原始的类实现来提供。
通过扩展添加的实例方法也可以修改该实例本身。结构体和枚举类型中**修改 self **或其属性的方法必须将该实例方法标注为 mutating ,正如来自原始实现的可变方法一样。
defer
延迟执行的操作会按照它们被指定时的顺序的相反顺序执行——也就是说,第一条 defer 语句中的代码会在第二条 defer 语句中的代码被执行之后才执行,以此类推。
Tip:Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。
访问级别 -- open , public , internal , fileprivate , private
• 开放访问
和公开访问
可以访问同一模块源文件中的任何实体,在模块外也可以通过导入该模块来访问源文件里的所有实体。通常情况下,框架中的某个接口可以被任何人使用时,你可以将其设置为开放或者公开访问。
• 内部访问
可以访问同一模块源文件中的任何实体,但是不能从模块外访问该模块源文件中的实体。通常情况下,某个接口只在应用程序或框架内部使用时,你可以将其设置为内部访问。
• 文件私有访问
限制实体只能被所定义的文件内部访问。当需要把这些细节被整个文件使用的时候,使用文件私有访问隐藏了一些特定功能的实现细节。
• 私有访问
限制实体只能在所定义的作用域内使用。需要把这些细节被整个作用域使用的时候,使用文件私有访问隐藏了一些特定功能的实现细节。
闭包的捕获列表
var a = 0
var b = 0
let closure = { [a] in
print(a, b)
}
a = 10
b = 10
closure()
// 打印 “0 10”
在示例中,变量 b 只有一个,然而,变量 a 有两个,一个在闭包外,一个在闭包内。
闭包内的变量 a 会在闭包创建时用闭包外的变量 a 的值来初始化,除此之外它们并无其他系。
这意味着在闭包创建后,改变某个a 的值都不会对另一个 a 的值造成任何影响。
与此相反,闭包内外都是同一个变量 b ,因此在闭包外改变其值,闭包内的值也会受影响。
函数或方法可以使用 rethrows 关键字来声明,从而表明仅当该函数或方法的一个函数类型的参数抛出错误时,该函数或方法才抛出错误。这类函数和方法被称为重抛函数和重抛方法。重新抛出错误的函数或方法必须至少有一个参数的类型为抛出函数。
@objc
协议可以定义可选要求,遵循协议的类型可以选择是否实现这些要求。在协议中使用 optional 关键字作为前缀来定义可选要求。可选要求用在你需要和 Objective-C 打交道的代码中。协议和可选要求都必须带上 @objc 属性。标记 @objc 特性的协议只能被继承自 Objective-C 类的类或者 @objc 类遵循,其他类以及结构体和枚举均不能遵循这种协议。
required
类在实现一个构造器去满足一个协议的构造器要求时,如果这个类还没有用 final 声明修饰符标记,这个构造器必须用 required 声明修饰符标记。
可以使用 required 声明修饰符,将便利构造器和指定构造器标记为每个子类都必须实现的构造器。这种构造器的子类实现也必须使用 required 声明修饰符标记。
网友评论