swift
1. Switches support any kind of data and a wide variety of comparison operations—they aren’t limited to integers and tests for equality.
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
3. closures in只起到分割作用
var numbers = [20, 19, 7, 12]
numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
4. closures的简明写法1
You have several options for writing closures more concisely. When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.
let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)
5. closures的简明写法2
You can refer to parameters by number instead of by name—this approach is especially useful in very short closures. A closure passed as the last argument to a function can appear immediately after the parentheses. When a closure is the only argument to a function, you can omit the parentheses entirely.
let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers)
6. 类每个Property必须初始化 类属性初始化有两种方式 Every property needs a value assigned—either in its declaration (as with numberOfSides
) or in the initializer (as with name
).
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
与 Objective-C 不同,Swift 的初始化方法需要保证类型的所有属性都被初始化。所以初始化方法的调用顺序就很有讲究。在某个类的子类中,初始化方法里语句的顺序并不是随意的,我们需要保证在当前子类实例的成员初始化完成后才能调用父类的初始化方法:
class Cat {
var name: String
init() {
name = "cat"
}
}class Tiger: Cat {
let power: Int
override init() {
power = 10
super.init()
name = "tiger"
}
}
一般来说,子类的初始化顺序是:
1.设置子类自己需要初始化的参数,power = 10
2.调用父类的相应的初始化方法,super.init()
3.对父类中的需要改变的成员进行设定,name = "tiger"
class Cat {
var name: String
init() {
name = "cat"
}
}class Tiger: Cat {
let power: Int
override init() {
power = 10
// 虽然我们没有显式地对 super.init() 进行调用
// 不过由于这是初始化的最后了,Swift 替我们完成了
}
}
9. 深入分析swift类初始化
我们在深入初始化方法之前,不妨先再想想Swift中的初始化想要达到一种怎样的目的。
其实就是安全。在Objective-C中,init方法是非常不安全的:没有人能保证init只被调用一次,也没有人保证在初始化方法调用以后,实例的各个变量都完成初始化,甚至如果在初始化里使用属性进行设置的话,还可能会造成各种问题。虽然Apple也明确说明了不应该在init中使用属性来访问,但这并不是编译器强制的,因此还是会有很多开发者犯这样的错误。
所以Swift有了超级严格的初始化方法。一方面,Swift强化了designated初始化方法的地位。Swift中不加修饰的init方法都需要在方法中保证所有非Optional的实例变量被赋值初始化,而在子类中也强制 (显式或隐式地)调用super版本的designated初始化,所以无论如何走何种路径,被初始化的对象总是可以完成完整的初始化的。
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
与designated初始化方法对应的是在init前加上convenience关键字的初始化方法。这类方法是Swift初始化方法中的“二等公民”,只作为补充和提供使用上的方便。所有的convenience初始化方法都必须调用同一个类中的designated初始化完成设置,另外convenience的初始化方法是不能被子类重写或从子类中以super的方式被调用的。
class ClassA {
let numA: Int
init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
override init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
只要在子类中实现重写了父类convenience方法所需要的init方法的话,我们在子类中就也可以使用父类的convenience初始化方法了。比如在上面的代码中,我们在ClassB里实现了init(num: Int)的重写。这样,即使在ClassB中没有bigNum版本的convenience init(bigNum: Bool),我们仍然还是可以用这个方法来完成子类初始化:
let anObj = ClassB(bigNum: true)
// anObj.numA = 10000, anObj.numB = 10001
因此进行一下总结,可以看到初始化方法永远遵循以下两个原则:
- 初始化路径必须保证对象完全初始化,这可以通过调用本类型的designated初始化方法来得到保证;
- 子类的designated初始化方法必须调用父类的designated方法,以保证父类也完成初始化。
对于某些我们希望子类中一定实现的designated初始化方法,我们可以通过添加required关键字进行限制,强制子类对这个方法重写实现。这样的一个最大的好处是可以保证依赖于某个designated初始化方法的convenience一直可以被使用。一个现成的例子就是上面的init(bigNum: Bool):如果我们希望这个初始化方法对于子类一定可用,那么应当将init(num: Int)声明为必须,这样我们在子类中调用init(bigNum: Bool)时就始终能够找到一条完全初始化的路径了:
class ClassA {let numA: Int
required init(num: Int) {
numA = num
}
convenience init(bigNum: Bool) {
self.init(num: bigNum ? 10000 : 1)
}
}
class ClassB: ClassA {
let numB: Int
required init(num: Int) {
numB = num + 1
super.init(num: num)
}
}
另外需要说明的是,其实不仅仅是对designated初始化方法,对于convenience的初始化方法,我们也可以加上required以确保子类对其进行实现。这在要求子类不直接使用父类中的convenience初始化方法时会非常有帮助。
10.swift之mutating关键字
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.”大致意思就是说,虽然结构体和枚举可以定义自己的方法,但是默认情况下,实例方法中是不可以修改值类型的属性。
举个简单的例子,假如定义一个点结构体,该结构体有一个修改点位置的实例方法:
struct Point {
var x = 0, y = 0
func moveXBy(x:Int,yBy y:Int) {
self.x += x
// Cannot invoke '+=' with an argument list of type '(Int, Int)'
self.y += y
// Cannot invoke '+=' with an argument list of type '(Int, Int)'
}
}
编译器抛出错误,说明确实不能在实例方法中修改属性值。
为了能够在实例方法中修改属性值,可以在方法定义前添加关键字 mutating
struct Point {
var x = 0, y = 0
mutating func moveXBy(x:Int,yBy y:Int) {
self.x += x
self.y += y
}
}
var p = Point(x: 5, y: 5)
p.moveXBy(3, yBy: 3)
另外,在值类型的实例方法中,也可以直接修改self属性值。
enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
case Off:
self = Low
case Low:
self = High
case High:
self = Off
}
}
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight is now equal to .High
ovenLight.next()
// ovenLight is now equal to .Off”
TriStateSwitch枚举定义了一个三个状态的开关,在next实例方法中动态改变self属性的值。
当然,在引用类型中(即class)中的方法默认情况下就可以修改属性值,不存在以上问题。
11. Protocols and Extensions