Swift常用语法示例代码(二)

此篇文章整理自我以前学习Swift时的一些练习代码,其存在的意义多是可以通过看示例代码更快地回忆Swift的主要语法。

如果你想系统学习Swift或者是Swift的初学者请绕路,感谢GithubThe Swift Programming Language开源翻译的中文版,感谢极客学院wiki提供的PDF版本。

代码和PDF版本上传至Github,有兴趣的可以下载下来试试。

SwiftEnum

Swift枚举相比C语言更加灵活,除了成员值,还可以对成员赋原始值和相关值。Swift的枚举采用了很多只被类支持的特征,如计算属性,构造函数,可以在原始的实现基础上扩展它们的功能,可依遵守协议来提供标准的功能。

import Foundation

// 使用enum关键词来创建枚举
enum CompassPoint {
    case North
    case South
    case East
    case West
}

// 多个成员值可以出现在同一行,用逗号隔开
enum Planet {
    case Mercury, Venus, Earth, Jupiter, Saturn, Uranus, Neptune
}

// directionToHead的类型由右侧类型推断,一旦类型确定,赋值可以省略枚举名字
var directionToHead = CompassPoint.West
directionToHead = .South
print(directionToHead)

// 使用switch语句匹配单个枚举值
switch directionToHead {
case .North :
    print("Lots of planets have a north")
case .South :
    print("Watch out for penguins")
case .East :
    print("Where the sun rise")
case .West :
    print("Where the skies blue")
}

// Swift的枚举允许对每个成员定义更多的信息
// 允许存储人意类型的相关值,如果需要的话每个成员的相关值类型可以各不相同
// 相关值在枚举定义时只声明类型,具体值在使用时初始化
enum Barcode {
    case UPCA(Int, Int, Int, Int)
    case QRCode(String)
}
var productBarcode = Barcode.UPCA(8, 85909, 51226, 3)
productBarcode = .QRCode("AFFJFLJSFJKSLJFSF")

// 在swith的cese分句中提取相关值
// 如果所有相关值都被提取成let或var,可以只放置一个let或var
switch productBarcode {
case .UPCA(let numberSystem, let manufacture, let product, let check) :
    print("UPC-A:\(numberSystem),\(manufacture),\(product),\(check)")
case let .QRCode(productCode):
    print("QR code:\(productCode)")
}

// 另外一种增加信息的方式是原始值(RawValue)
// 原始值的类型和值都需要在定义时确定,不同case的原始值具有相同的类型
// 原始值可以是字符串,字符,或者任何整数型或浮点数值
enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

// 使用整数作为原始值时,隐式赋值会依次递增1,如果第一个值没有被赋初值,将自动被赋值为0
// 使用字符串作为原始值时,隐式赋值为枚举成员名称
enum AnotherPlanet: Int {
    case Mercury = 1, Venus, Earth, Jupiter, Saturn, Uranus, Neptune
}
enum AnotherCompassPoint: String {
    case North, South, East, West
}

// 使用枚举成员的rawValue属性可以访问成员的原始值
print(AnotherPlanet.Earth.rawValue)

// 使用原始值来初始化枚举变量,因为有可能失败,所以返回可选类型
let positionToFind = 9
if let possiblePlanet = AnotherPlanet(rawValue: positionToFind) {
    switch possiblePlanet {
    case .Earth:
        print("Mostly harmless")
    default:
        print("Not a safe place for humans")
    }
} else {
    print("There isn't a planet at position \(positionToFind)")
}

// 递归枚举,一个或多个枚举成员使用该枚举类型的实例作为关联值
// 在枚举成员前加上indirect来表示该成员可递归
// 你也可以在枚举类型开头加上indirect关键字来表明它的所有成员都是可递归的
indirect enum ArithmeticExpression {
    case Number(Int)
    case Addition(ArithmeticExpression, ArithmeticExpression)
    case Multiplication(ArithmeticExpression, ArithmeticExpression)
}
func evaluate(expression: ArithmeticExpression)-\>Int {
    switch expression {
    case.Number(let value) :
        return value
    case.Addition(let left, let right) :
        return evaluate(left) + evaluate(right)
    case.Multiplication(let left, let right):
        return evaluate(left) * evaluate(right)
    }
}
let five = ArithmeticExpression.Number(5)
let four = ArithmeticExpression.Number(4)
let sum = ArithmeticExpression.Addition(five, four)
let product = ArithmeticExpression.Multiplication(sum, ArithmeticExpression.Number(2))
print(evaluate(product))

SwiftClass

与其它编程语言所不同的是,Swift并不要求你为类和结构创建独立的借口和实现文件,系统会自动生成面向其它代码的外部接口。

类和结构体的共同点:

  • 定义属性用于存储值
  • 定义方法用于提供功能
  • 定义附属脚本用于访问值
  • 定义构造器用于初始化值
  • 通过扩展以增加默认实现的功能
  • 通过协议以提供某种标准功能

与结构体相比,类还有如下的附加功能:

  • 继承允许一个类继承另一个类的特征
  • 类型转换允许在运行时检查和解释一个类实例的类型
  • 解构器允许一个类实例释放任何其所被分配的资源
  • 引用计数允许对一个类的多次引用
import Foundation

// 类和结构体由着类似的定义方法,通过class和struct关键字来定义
// 每定义一个新类和结构体,实际上定义了一个新的Swift类型,因此请使用首字母大写为类或结构体命名

struct Resolution {
    var width = 0
    var height = 0
}
// name属性会自动被赋予一个默认值nil,意为“没有name值”,因为它是一个可选类型
class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}
// 使用默认构造函数创建实例,其属性值会被初始化为默认值
let someResolution = Resolution()
let someVideoMode = VideoMode()

// 访问实例中的属性,为属性赋值
print("The width of someResolution is \(someResolution.width)")
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is \(someVideoMode.resolution.width)")

// 所有结构体都会自动生成成员逐一构造器,类实例没有默认的成员逐一构造器
let vga = Resolution(width: 640, height: 480)

// 结构体和枚举是值类型,值类型被赋予一个常量、变量或者本身被传递给一个函数的时候,实际上的操作是拷贝
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
print(cinema, hd)

// 类是引用类型,在被赋予一个常量、变量或者本身被传递给一个函数的时候,实际上的操作是引用
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
print(alsoTenEighty.frameRate, tenEighty.frameRate)

// 恒等运算符,判断两个常量或变脸是否引用同一个类实例
if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the sanme instance")
}

// Swift中String Array Dictionary均以结构体的形式实现

SwiftProperties

Swift广义的类包含Class、Struct、Enum。Swift的类内值分为存储属性和计算属性两大类,存储属性又分普通存储属性和延迟存储属性。又可以根据属性属于实例还是类分为类型属性和实例属性。除了延迟属性,均可对属性添加属性观察者。

import Foundation

// 属性将值与特定的类、结构体和枚举关联。
// 存储属性存储常量或变量作为实例的一部分,只能用于类和结构体
// 计算属性计算一个值,计算属性可以用于类、结构体和枚举
// 存储属性和计算属性通常与特定类型的实例关联,但属性也可以直接作用于类型本身,称为类型属性
// 还可以定义属性观察者来监控属性值的变化,以触发一个自定义的操作

// 存储属性
struct FixedLengthRange {
    var firstVaule: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstVaule: 0, length: 3)
rangeOfThreeItems.firstVaule = 6

// 常量结构体的属性无法修改,即使属性是变量。
// 结构体是值类型,值类型的实例被声明为常量时,它的所有属性也成了常量
// 属于引用的类则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性
let rangeOfFourItems = FixedLengthRange(firstVaule: 0, length: 4)
// Error: rangeOfFourItems.firstVaule = 6
class SomeClass {
    var firstVaule = 0
    let secondValue = 0
}
let aSomeClass = SomeClass()
aSomeClass.firstVaule = 8

// 延迟存储属性,用关键字lazy声明,必须将延迟存储属性声明为变量
class DataImporter {
    /*
    DataImporter是一个把外部文件中的数据导入的类
    这个类的初始化会消耗不少时间
    */
    var fileName = "data.txt"
}
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // 这里是数据管理功能
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// importer属性还没有被创建,直到第一次被访问才被创建
print(manager.importer.fileName)


// 类、结构体和枚举都可以定义计算属性
// 计算属性并不直接存储值,而是提供一个getter和一个可选的setter方法来间接获取和设置其它属性的值
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    // 计算属性应该定义成变量,因为它的值不固定,即便是只读计算属性
    var center: Point {
        get {
            let centerX = origin.x + (size.width) / 2
            let centerY = origin.y + (size.height) / 2
            return Point(x: centerX, y: centerY)
        }
        // 如果计算属性只有getter没有setter,该计算属性就是只读计算属性
        // 只读计算属性在声明中可以去掉get关键词和花括号
        set(newCenter) {
            origin.x = newCenter.x - (size.width) / 2
            origin.y = newCenter.y - (size.height) / 2
            // 更便捷的写法是,省略定义Setter参数,用默认的newValue代替
        }
    }
}
var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("Square.origin is now at (\(square.origin.x),\(square.origin.y))")


// 属性观察者监控和响应属性值的变化。每次属性被设置的时候都会调用属性观察者,即便新值和旧值有时不同
// 除了延迟存储属性外的其它属性均可设置属性观察者
// 可以为通过属性重载方式继承来的属性添加属性观察者
// willSet观察者会将新的属性值以常量参数传入,默认为newValue
// didSet会将旧的属性值作为参数传入,默认为oldValue
class StepCounter {
    var totalSteps: Int = 0 {
        willSet {
            print("about to set totalSteps to \(newValue)")
        }
        didSet {
            if totalSteps > oldValue {
                print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
stepCounter.totalSteps = 360


// 属性观察者的方法也可以用于全局变量或局部变量
// 全局的常量或变量都是延迟计算的,与延迟存储属性相似。局部常量或变量不会延迟计算


// 存储型类型属性必须指定默认值
struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannels = 0
    var currentLevel: Int = 0 {
        didSet {
            if currentLevel > AudioChannel.thresholdLevel {
                currentLevel = AudioChannel.thresholdLevel
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChannels {
                AudioChannel.maxInputLevelForAllChannels = currentLevel
            }
        }
    }
}
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
print(AudioChannel.maxInputLevelForAllChannels)

SwiftMethods

Swift允许在类、结构体和枚举上创建方法,方法分为实例方法和类型方法两大类,这部分内容相对简单,方法的语法与函数完全相同。

import Foundation

// 实例方法,语法与函数完全相同,能隐式地访问它所属类型的其它实例方法和属性
class Counter1 {
    var count = 0
    func increment() {
        ++count
    }
    func incrementBy(amount:Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}
let counter1 = Counter1()
counter1.increment()
counter1.incrementBy(5)
print(counter1.count)
counter1.reset()
print(counter1.count)

// 方法的局部参数名称和外部参数名称,语法类似函数
class Counter2 {
    var count = 0
    func incrementBy(amount: Int, numberOfTimes: Int) {
        count += amount * numberOfTimes
    }
}
let counter2 = Counter2()
// Swift只把amount作为局部名称,但是把numberOfTimes即看作局部名称又看作外部名称
// 可以定义时修改外部参数名或者用\_隐藏外部参数名
counter2.incrementBy(5, numberOfTimes: 3)

// 类型的每一个实例都有一个隐含的属性叫做self,你可以在一个实例方法中使用self属性来引用当前实例
// 主要用于实例方法的某个参数名称与实例的某个属性名称相同的情况。
struct Point1 {
    var x = 0.0, y = 0.0
    func isToTheRightOfX(x: Double)->Bool {
        return self.x > x
    }
}
let somePoint1 = Point1(x: 4.0, y: 5.0)
if somePoint1.isToTheRightOfX(1.0) {
    print("The point is to the right of the line ")
}

// 一般情况下,结构体和枚举的属性不能在它的实例方法中被修改
// 使用关键字mutating声明方法可改变属性
// mutating方法甚至可以更改self属性的值
// 结构体常量不能调用mutating方法
struct Point2 {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}
var somePoint2 = Point2(x: 1.0, y: 1.0)
somePoint2.moveByX(2.0, y: 3.0)
struct Point3 {
    var x = 0.0, y = 0.0
    mutating func moveByX(deltaX: Double, y deltaY: Double) {
        self = Point3(x: x + deltaX, y: y + deltaY)
    }
}
var somePoint3 = Point3(x: 1.0, y: 1.0)
somePoint3.moveByX(2.0, y: 3.0)
enum TriStateSwitch {
    case Off, Low, High
    mutating func next(){
        switch self {
        case .Off :
            self = .Low
        case .Low :
            self = .High
        default :
            self = .Off
        }
    }
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
print(ovenLight)

// 在方法前加上static关键字声明结构体和枚举的类型方法
// 类可能会用关键字class来允许子类重写父类的实现方法
struct LevelTracker {
    static var highestUnlockedLevel = 1
    static func unLockLevel(level: Int) {
        if level > highestUnlockedLevel {
            highestUnlockedLevel = level
        }
    }
    static func levelIsUnlocked(level: Int) ->Bool {
        return level <= highestUnlockedLevel
    }
    var currentLevel = 1
    mutating func advanceToLevel(level: Int)->Bool {
        if LevelTracker.levelIsUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

SwiftSubscripts

Swift下标脚本相当于其它语言中的[]运算符重载,语法类似于计算属性,是一种对访问集合列表和序列的一种常见的快捷操作,注意不要忘了索引值合理性的判定。

import Foundation

// 下标脚本(subscript)可以定义在类、结构体和枚举中,作为访问集合、列表或序列的快捷方式
// 允许通过在实例后面的方括号传入一个或者多个索引值来对实例进行访问和赋值
struct TimesTable {
    let multiplier: Int
    subscript(index: Int) ->Int {
        return multiplier * index
    }
}
let threeTimesTable = TimesTable(multiplier: 3)
print(threeTimesTable[6])

// 下标脚本的参数个数没有限制,可以使用可变参数,但参数默认值和in-out参数不被允许
// 可以定义重载的下标脚本
struct Matrix {
    let rows: Int, columns: Int
    var grid: [Double]
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(count: rows * columns, repeatedValue: 0.0)
    }
    func indexIsValidForRow(row: Int, column: Int)->Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }
    subscript(row: Int, column: Int)->Double {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[row * columns + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[row * columns + column] = newValue
        }
    }
}
var matrix = Matrix(rows: 2, columns: 2)
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
print(matrix.grid)
// matrix[0, 3] = 1.5

SwiftInheritance

一个类可以继承(inherit)另一个类的方法(methods),属性(properties)和其它特性。在 Swift 中,继承是区分「类」与其它类型的一个基本特征。

在 Swift 中,类可以调用和访问超类的方法,属性和下标脚本(subscripts),并且可以重写(override)这些方法,属性和下标脚本来优化或修改它们的行为。Swift 会检查你的重写定义在超类中是否有匹配的定义,以此确保你的重写行为是正确的。

可以为类中继承来的属性添加属性观察器(property observers),这样一来,当属性值改变时,类就会被通知到。可以为任何属性添加属性观察器,无论它原本被定义为存储型属性(stored property)还是计算型属性(computed property)。

import Foundation

// 不集成自其它的类,我们称之为基类
class Vehicle {
    var currentSpeed = 0.0
    var description: String {
        return "traveling at \(currentSpeed) miles per hour"
    }
    func makeNoise() {
        print("Don't Know")
    }
}
let someVehicle = Vehicle()
print(someVehicle.description)

// 定义一个子类,冒号:后面表示继承的类(父类)
class Bicycle: Vehicle {
    var hasBasket = false // 增加一个属性
}
let bicycle = Bicycle()
bicycle.hasBasket = true

// 可以访问继承自父类的属性
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")

class Tandem: Bicycle {
    var currentNumberOfPassengers = 0
}
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")

// 重写方法
class Train: Vehicle {
    override func makeNoise() {
        print("Choo Choo")
    }
}
let train = Train()
train.makeNoise()

// 重写属性, 采用super.访问父类方法或属性
class Car: Vehicle {
    var gear = 1
    override var description: String {
        return super.description + " in gear \(gear)"
    }
}
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")

// 重写属性观察者
class AutomaticCar: Car {
    override var currentSpeed: Double {
        didSet {
            gear = Int(currentSpeed / 10.0) + 1
        }
    }
}
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
automatic.makeNoise()

// 防止重写
// 你可以通过把方法,属性或下标脚本标记为final来防止它们被重写
// 你可以通过在关键字class前添加final修饰符来将整个类声明为final,不允许继承

SwiftInitialization

SwiftInitialization是对Swift构造函数的介绍,Swift的构造函数大部分语法特性与其它语言类似,需要注意的是可失败构造器、便利构造器和必要构造器这些特殊的地方,内容相对较多,但都容易理解。

import Foundation

// 类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。
// 使用关键词init创建一个构造器
struct Fahrenheit {
    var temperature: Double // 也可以为存储属性设置类内默认值
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature) Fahrenheit")

// 构造参数
struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromkelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingpointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromkelvin: 273.15)

struct Color {
    let red, green, blue: Double
    init(red: Double, green: Double, blue: Double) { // 可用_隐藏外部参数名
        self.red = red
        self.green = green
        self.blue = blue
    }
    init(white: Double) {
        red = white
        green = white
        blue = white
    }
}
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)

// 类型包含一个逻辑上允许取值为空的存储类型,需要定义为可选类型。
// 可选类型自动初始化为nil
// 构造过程中可以改变常量属性的值,一旦常量属性被赋值,它将永远不可更改
class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese")
cheeseQuestion.ask()
cheeseQuestion.response = "Yes, I do like cheese"

// 默认构造器
// 结构体或类的所有属性值都有默认值且没有自定义构造器,则Swift会提供一个默认构造器为所有的属性值设置为默认值
// 如果你自定义了构造器,默认构造器将会失效。可以通过扩展写自定义构造器来防止默认构造器失效。
class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()

// 结构体提供特殊的逐一成员构造器
struct Size {
    var width = 0.0
    var height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

// 构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理
// 构造器代理对于值类型和类类型有区别,值类型只能代理自身别的构造器
// 使用self.init在自定义的构造器中引用类型中的其它构造器
struct Point {
    var x = 0.0, y = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

let basicRect = Rect()
let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))

// Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便 利构造器。
// 一个指定构造器将初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化
// 每一个类都必须拥有至少一个指定构造器.你可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值
class Vehicle {
    var numberOfWheels = 0
    var description: String {
    return "\(numberOfWheels) wheel(s)"
    }
}
let vehicle = Vehicle()
print("Vehicle: \(vehicle.description)")
class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
        }
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")

class Food {
    var name: String
    init(name: String) {
    self.name = name
    }
    convenience init() {
    self.init(name: "[Unnamed]")
    } }
let namedMeat = Food(name: "Bacon")
let mysteryMeat = Food()

class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
    self.quantity = quantity
    super.init(name: name)
    }
    override convenience init(name: String) {
    self.init(name: name, quantity: 1)
    } }
let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

class ShoppingListItems: RecipeIngredient {
            var purchased = false
            var description: String {
            var output = "\(quantity) x \(name)"
            output += purchased ? " ?" : " ?"
            return output
            } }
// 由于它为自己引入的所有属性都提供了默认值,并且自己没有定义任何构造器, ShoppingListItem 将自动继承所 有父类中的指定构造器和便利构造器。
var breakfastList = [
    ShoppingListItems(),
    ShoppingListItems(name: "Bacon"),
    ShoppingListItems(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
    print(item.description)
}

// 可失败构造器。其语法为在 init 关键字后面加添问号 (init?) 
struct Animal {
    let species: String
    init?(species: String) {
    if species.isEmpty { return nil }
    self.species = species
    }
}
let someCreature = Animal(species: "Giraffe")
print(someCreature)
if let giraffe = someCreature {
        print("An animal was initialized with a species of \(giraffe.species)")
}
let anonymousCreature = Animal(species: "")

if anonymousCreature == nil {
        print("The anonymous creature could not be initialized")
}

// 枚举类型的可失败构造器
enum TemperatureUnit {
    case Kelvin, Celsius, Fahrenheit
    init?(symbol: Character) {
    switch symbol {
case "K":
    self = .Kelvin
case "C":
    self = .Celsius
case "F":
    self = .Fahrenheit
default:
    return nil }
    } }
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

// 带原始值的枚举类型会自带一个可失败构造器 init?(rawValue:)
enum TemperatureUnit1: Character {
        case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahrenheitUnit1 = TemperatureUnit1(rawValue: "F")

if fahrenheitUnit1 != nil {
    print("This is a defined temperature unit, so initialization succeeded.")
}
let unknownUnit1 = TemperatureUnit1(rawValue: "X")
if unknownUnit == nil {
    print("This is not a defined temperature unit, so initialization failed.")
}

// 类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。
// Product 类的所有可失败构造器必须在自己 失败前给 name 属性一个初始值
class Product {
    let name: String!
    init?(name: String) {
    self.name = name
    if name.isEmpty { return nil }
    }
}
class CartItem: Product {
    let quantity: Int!
    init?(name: String, quantity: Int) {
    self.quantity = quantity
    super.init(name: name)
    if quantity < 1 { return nil }
    }
}
if let zeroShirts = CartItem(name: "shirt", quantity: 0) {
    print("Item: \(zeroShirts.name), quantity: \(zeroShirts.quantity)")
} else {
    print("Unable to initialize zero shirts")
}
if let oneUnnamed = CartItem(name: "", quantity: 1) {
    print("Item: \(oneUnnamed.name), quantity: \(oneUnnamed.quantity)")
} else {
    print("Unable to initialize one unnamed product")
}

// 重写一个可失败构造器
class Document {
    var name: String!
    // 该构造器构建了一个name属性值为nil的document对象
    init() {}
    // 该构造器构建了一个name属性值为非空字符串的document对象
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}
class AutomaticallyNamedDocument: Document {
    override init() {
    super.init()
    self.name = "[Untitled]"
    }
    override init(name: String) {
    super.init()
    if name.isEmpty {
    self.name = "[Untitled]"
} else {
    self.name = name
    }
}
}
class UntitledDocument: Document {
    override init() {
    // 你可以在构造器中调用父类的可失败构造器强制解包,以实现子类的非可失败构造器
    super.init(name: "[Untitled]")!
    }
}

struct Checkerboard {
    let boardColors: [Bool] = {
        var temporaryBoard = [Bool]()
        var isBlack = false
        for i in 1...10 {
            for j in 1...10 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
        isBlack = !isBlack
        }
        return temporaryBoard
    }()
    func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
        return boardColors[(row * 10) + column]
    }
}
let board = Checkerboard()
print(board.squareIsBlackAtRow(0, column: 1))
print(board.squareIsBlackAtRow(9, column: 9))


SwiftDeinitialization

SwiftDeinitialization是对Swift析构函数的介绍,Swift析构器只适用于类类型,通常当你的实例被释放时不需要手动地去清理,但是,当使用自己的资源时,你可能 需要进行一些额外的清理。析构器会自动调用父类析构器。

import Foundation

// 简单的析构器例子。
struct Bank {
    static var coinsInBank = 10_000
    static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
        numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
        return numberOfCoinsToVend
    }
    static func receiveCoins(coins: Int) {
        coinsInBank += coins
    }
}
class Player {
    var coinsInPurse: Int
    init(coins: Int) {
        coinsInPurse = Bank.vendCoins(coins)
    }
    func winCoins(coins: Int) {
        coinsInPurse += Bank.vendCoins(coins)
    }
    deinit {
        Bank.receiveCoins(coinsInPurse)
    }
}
var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
print("There are now \(Bank.coinsInBank) coins left in the bank")

playerOne!.winCoins(2\_000)
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
print("The bank now only has \(Bank.coinsInBank) coins left")


SwiftARC

Swift使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。通常情况下,Swift 内存管理机制会一
直起作用,你无须自己来考虑内存的管理。ARC 会在类的实例不再被使用时,自动释放其占用的内存。然而,在少数情况下,ARC 为了能帮助你管理内存,需要更多的关于你的代码之间关系的信息。

import Foundation

// Swift使用自动引用计数(ARC)机制来跟踪和管理你的应用程序的内存。
// 引用计数仅仅应用于类的实例。结构体和枚举类型是值类型,不是引用类型,也不是通过引用的方式存储和传递。

// ----------------- Part 1 -------------------------
// 下面的例子展示了自动引用计数的工作机制。
class Person {
    let name: String
    init(name: String) {
        self.name = name
        print("\(name) is being initialized")
    }
    deinit {
    print("\(name) is being deinitialized")
    }
}
var reference1: Person?
var reference2: Person?
var reference3: Person?

reference1 = Person(name: "John Appleseed")
// reference1到Person类的新实例之间建立了一个强引用
// 正是因为这一个强引用,ARC 会保证 Person 实例被保持在内存中不被销毁

reference2 = reference1
reference3 = reference2
// 现在这一个Person实例已经有三个强引用了

reference1 = nil
reference3 = nil
// 给其中两个变量赋值 nil 的方式断开两个强引用(包括最先的那个强引用),只留下一个强引用, Person实例不会被销毁

reference2 = nil
// 最后一个强引用被断开,Person实例被销毁


// ----------------- Part 2 -----------------
// 循环强引用问题,一个类实例的强引用数永远不能变成0
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?

john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
// 变量john现在有一个指向Person实例的强引用,而变量unit4A有一个指向Apartment实例的强引用

john!.apartment = unit4A
unit4A!.tenant = john
// 这两个实例关联后会产生一个循环强引用

john = nil
unit4A = nil
// 当你断开john和unit4A引用时,引用计数并不会减为0,实例也不会被ARC销毁


// ----------------- Part 3 -----------------
// Swift提供了两种办法用来解决你在使用类的属性时所遇到的循环强引用问题:弱引用(weak reference)和无主引用(unowned reference)。
class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { print("\(name) is being deinitialized") }
}
class Apartment {
    let unit: String
    init(unit: String) { self.unit = unit }
    weak var tenant: Person?
    deinit { print("Apartment \(unit) is being deinitialized") }
}

// 跟之前一样,建立两个变量( john 和 unit4A )之间的强引用,并关联两个实例
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
// Person实例依然保持对Apartment实例的强引用,但是Apartment实例只是对Person实例的弱引用。
// 这意味着当你断开john变量所保持的强引用时,再也没有指向Person实例的强引用了

john = nil
// 唯一剩下的指向Apartment实例的强引用来自于变量unit4A。如果你断开这个强引用,再也没有指向Apartment实例的强引用了
unit4A = nil
// 首先断开unit4A的对Apartment实例的强引用,并不会使得Apartment实例销毁,因为此时Person实例依旧有对Apartment实例的强引用


// ----------------- Part 4 -----------------
// 下面的例子定义了两个类, Customer 和 CreditCard ,模拟了银行客户和客户的信用卡。
// 这两个类中,每一个都将另外一个类的实例作为自身的属性。这种关系可能会造成循环强引用。
class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) {
        self.name = name
    }
    deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    deinit { print("Card #\(number) is being deinitialized") }
}

var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234\_5678\_9012\_3456, customer: john!)
// Customer 实例持有对 CreditCard 实例的强引用,而 CreditCard 实例持有对 Customer 实例的无主引用。

john = nil


// ----------------- Part 4 -----------------
//Person和Apartment的例子展示了两个属性的值都允许为nil,并会潜在的产生循环强引用。这种场景最适合用弱引用来解决。
//Customer和CreditCard的例子展示了一个属性的值允许为nil,而另一个属性的值不允许为nil,这也可能会产生循环强引用。这种场景最适合通过无主引用来解决。
//存在着第三种场景,在这种场景中,两个属性都必须有值,并且初始化完成后永远不会为nil。在这种场景中,需要一个类使用无主属性,而另外一个类使用隐式解析可选属性。
class Country {
    let name: String
    var capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
    deinit { print("Country \(name) is being deinitialized") }
}
class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
    deinit { print("City \(name) is being deinitialized") }
}
var country = Country(name: "Canada", capitalName: "Ottawa")
print("\(country.name)'s capital city is called \(country.capitalCity.name)")
// 以上的意义在于你可以通过一条语句同时创建Country和City 的实例,而不产生循环强引用,并且capitalCity的属性能被直接访问,而不需要通过感叹号来展开它的可选值
country = Country(name: "China", capitalName: "Beijing")


// ----------------- Part 5 -----------------
// 循环强引用还会发生在当你将一个闭包赋值给类实例的某个属性,并且这个闭包体中又使用了这个类实例。
// 这个闭包体中可能访问了实例的某个属性,例如self.someProperty,或者闭包中调用了实例的某个方法,例如 self.someMethod 。
// 这两种情况都导致了闭包 “捕获" self ,从而产生了循环强引用。
// 循环强引用的产生,是因为闭包和类相似,都是引用类型
class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: Void -> String = {
        if let text = self.text {
        return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}

var heading = HTMLElement(name: "h1")
let defaultText = "some default text"
heading.asHTML = {
    return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
}
print(heading.asHTML())
heading = HTMLElement(name: "head")

//  HTMLElement类产生了类实例和asHTML默认值的闭包之间的循环强引用。
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil


// ----------------- Part 6 -----------------
// Swift提供了一种优雅的方法来解决这个问题,称之为闭包捕获列表(closuer capture list)
// 在定义闭包时同时定义捕获列表作为闭包的一部分,捕获列表定义了闭包体内捕获一个或者多个引用类型的规则
// Swift有如下要求:只要在闭包内使用self的成员,就要用self.someProperty(而非someProperty)。这提醒你可能会一不小心就捕获了self。
// 捕获列表中的每一项都由一对元素组成,一个元素是unowned或weak关键字。
// 另一个元素是类实例的引用(如self)或初始化过的变量(如self.someProperty)
class HTMLElement {
    let name: String
    let text: String?
    lazy var asHTML: Void -> String = {
        [unowned self] in
        if let text = self.text {
            return "<\(self.name)>\(text)</\(self.name)>"
        } else {
            return "<\(self.name) />"
        }
    }
    init(name: String, text: String? = nil) {
        self.name = name
        self.text = text
    }
    deinit {
        print("\(name) is being deinitialized")
    }
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil

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

推荐阅读更多精彩内容