美文网首页
Swift 语法(三)

Swift 语法(三)

作者: 对歌当酒 | 来源:发表于2016-07-26 15:03 被阅读38次

    枚举

    • 声明
    //enum 关键字
    enum Season { //新的数据类型,首字母大写
        case Spring
        case Summer
        case Autumn
        case Winter
    }
    
    //也可以这样简写
    enum Season {
        case Spring, Summer, Autumn, Winter
    }
    
    • 获取
    var season = Season.Summer
    var season:Season = Season.Summer //显式
    var season:Season = .Summer //可以这么简写
    
    • 原始值

    可以给枚举变量赋原始值 (Raw Value),例如:

    enum Fruit:Int {
        case Apple = 1
        case Orange = 2
        case Banana = 3
        case Watermelon = 4
    }
    
    let fruit = Fruit(rawValue: 2) //返回值为可选型
    Fruit.Watermelon.rawValue //4
    
    //解包
    if let fruit = Fruit(rawValue: 2) {
        //do something
    }
    

    此外,关于原始值,还有其他用法,例如:

    //可以只写第一个,后面的会依次加1
    enum Fruit:Int {
        case Apple = 1, Orange, Banana, Watermelon
    }
    
    //也可以都不写,则默认从0开始,依次加1
    enum Fruit:Int {
        case Apple, Orange, Banana, Watermelon
    }
    
    //定义是整型值的也可以不连续
    enum Coin:Int {
        case Penny = 1
        case Nickel = 5
        case Dime = 10
        case Quarter = 25
    }
    
    //枚举类型的值可以是 String 类型,例如:
    enum ProgrammingLanguage:String {
        case Swift = "Swift"
        case Java = "Java"
        case OC = "OC"
    }
    
    //若不初始化,则默认是定义的字符
    enum ProgrammingLanguage:String {
        case Swift, Java, OC //即,分别是 "Swift", "Java", "OC"
    }
    
    • 关联值

    关联值(Associate Value):可以关联不同类型,而且可修改(与 Raw Value 互斥),例如:

    enum ATMStatus {
        case Success(Int)
        case Error(String)
    }
    
    //也可有部分没有关联值
    enum ATMStatus {
        case Success(Int)
        case Error(String)
        case Waiting //无关联值
    }
    

    使用举例:

    var balance = 1000 //余额
    func withdraw(amount:Int) -> ATMStatus {
        if balance >= amount {
            balance -= amount
            return .Success(balance) //可以这样简写
        }
        else {
            return .Error("Not enough money")
        }
    }
    
    let result = withdraw(100)
    switch result {
    case let .Success(newBalance):
        print("¥\(newBalance) left in your count")
    case let .Error(errorMessage):
        print("Error: \(errorMessage)")
    }
    

    此外,还可以关联多个值(其实是关联了一个元组),例如:

    enum Shape {
        case Square(side:Double) //可以分别关联不同的值
        case Rectangle(width:Double, height:Double)
        case Circle(centerX:Double, centerY:Double, radius:Double)
        case Point
    }
    
    func area(shape:Shape) -> Double {
        switch shape {
        case let .Square(side):
            return side * side
        case let .Rectangle(width, height):
            return width * height
        case let .Circle( _, _, radius): //忽略一些变量
            return M_PI * radius * radius //M_PI 为 π
        case .Point:
            return 0
        }
    }
    
    //使用
    let square = Shape.Square(side: 3)
    let rectangle = Shape.Rectangle(width: 5, height: 3)
    let circle = Shape.Circle(centerX: 6, centerY: 7, radius: 3)
    let point = Shape.Point
    
    • 递归枚举
    //使用关键字 indirect
    indirect enum ArithmeticExpression {
        case Number(Int)
        case Addition(ArithmeticExpression, ArithmeticExpression) //调用了本身
        case Multiplication(ArithmeticExpression, ArithmeticExpression)
    }
    
    //或者这样写
    enum ArithmeticExpression2 {
        case Number(Int)
        indirect case Addition(ArithmeticExpression, ArithmeticExpression)
        indirect case Multiplication(ArithmeticExpression, ArithmeticExpression)
    }
    
    //计算 (5 + 4) * 2 举例:
    let five = ArithmeticExpression.Number(5)
    let four = ArithmeticExpression.Number(4)
    let sum = ArithmeticExpression.Addition(five, four)
    let two = ArithmeticExpression.Number(2)
    let product = ArithmeticExpression.Multiplication(sum, two)
    
    func evaluate(expression:ArithmeticExpression) -> Int {
        switch expression {
        case let .Number(value):
            return value
        case let .Addition(left, right):
            return evaluate(left) + evaluate(right)
        case let .Multiplication(left, right):
            return evaluate(left) * evaluate(right)
        }
    }
    
    evaluate(product)
    evaluate(sum)
    
    • 修改自身变量

    枚举中,若想通过方法对自身变量进行修改,需要使用 mutating 关键字,例如:

    enum Switch {
        case On
        case Off
        
        mutating func click() {
            switch self {
            case .On:
                self = .Off
            case .Off:
                self = .On
            }
        }
    }
    

    结构体

    • 声明
    //struct 关键字
    struct Location { //新的数据类型,首字母大写
        let latitude:Double //若不初始化,则默认没有值。且 let 只能初始化一次
        let longitude:Double
        var placeName:String? //若为可选型,默认初始化为 nil 
    }
    
    //初始化一个结构体(调用了默认的构造函数,参数顺序不能变)
    let appleHeadQuarterLocation = Location(latitude: 37.3230, longitude: -122.0322) 
    //注意:只有 appleHeadQuarterLocation 和 latitude 都为 var 类型时才能对 latitude 进行修改。
    
    //结构体中变量的类型也可以是结构体,例如:
    struct Place {
        let location:Location //Location 为结构体类型
        var name:String
    }
    
    • 构造函数
    struct Location2 {
        var latitude:Double = 0 //可以赋初值
        var longitude:Double = 0
    }
    
    Location2() //赋初值后可以这样使用,调用了默认的构造函数
    Location2().latitude
    

    自定义构造函数:

    struct Location3 {
        let latitude:Double
        let longitude:Double
        
        //自定义构造函数 (使用 init 关键字)
        init(coordinateString: String){
            let commaIndex = coordinateString.rangeOfString(",")!.startIndex //这里暂时使用了强制解包,后文再解决这个问题
            let firstElement = coordinateString.substringToIndex(commaIndex)
            let secondElement = coordinateString.substringFromIndex(commaIndex.successor())
            
            latitude = Double(firstElement)!
            longitude = Double(secondElement)!
        }
        //注意:若添加了自定义的构造函数后,默认的构造函数就不能用了
        //此时,建议再写出默认的构造函数,即:
        init(latitude:Double, longitude:Double){
            self.latitude = latitude
            self.longitude = longitude
        }
    }
    
    • 可失败的构造函数

    结构体可以有可失败的构造函数(Failable-Initializer ),即,如果构造失败,返回为 nil。例如:

    struct Location {
        let latitude:Double
        let longitude:Double
        
        //可失败的构造函数
        init?(coordinateString: String){
            if let commaIndex = coordinateString.rangeOfString(",")?.startIndex {
                if let firstElement = Double(coordinateString.substringToIndex(commaIndex)) {
                    if let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor())) {
                        self.latitude = firstElement
                        self.longitude = secondElement
                    }
                    else {
                        return nil
                    }
                }
                else {
                    return nil
                }
            }
            else {
                return nil
            }
        }
        
        init(latitude:Double, longitude:Double){
            self.latitude = latitude
            self.longitude = longitude
        }
    }
    

    上述构造函数使用了多个 if...else 语句,看起来很复杂。我们可以使用 guard 关键字来简化,使代码条理更清晰。例如:

    struct Location {
        ...
        
        init?(coordinateString: String){
            //使用 guard 关键字可以使条理更清晰
    //        guard let commaIndex = coordinateString.rangeOfString(",")?.startIndex else {
    //            return nil
    //        }
    //        guard let firstElement = Double(coordinateString.substringToIndex(commaIndex)) else {
    //            return nil
    //        }
    //        guard let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor())) else {
    //            return nil
    //        }
    
            //还可以更加简洁的这样写
            guard
                let commaIndex = coordinateString.rangeOfString(",")?.startIndex,
                let firstElement = Double(coordinateString.substringToIndex(commaIndex)),
                let secondElement = Double(coordinateString.substringFromIndex(commaIndex.successor()))
            else {
                return nil
            }
            
            self.latitude = firstElement
            self.longitude = secondElement
        }
        
        ...
    }
    
    • 修改自身变量

    同枚举一样,结构体中,若想使用方法对自身变量进行修改,也需要使用关键字 mutating,例如:

    struct Location {
        var x = 0
        mutating func go() { //自己改变自己
            self.x += 1
        }
    }
    

    • 声明

    Swift 中的类和结构体很相似。使用关键字 class,示例如下:

    class Person {
        //成员变量
        var firstName:String
        var lastName:String
        var career:String? //可选型变量可以不初始化,默认为 nil
     
        //构造函数
        init(firstName:String, lastName:String){
            self.firstName = firstName
            self.lastName = lastName
        }
    }
    
    • 可失败的构造函数

    同结构体一样,类也有可失败的构造函数,构造对象失败后返回 nil。例如:

    init?(fullName:String){
        guard
            let spaceindex = fullName.rangeOfString(" ")?.startIndex
        else {
            return nil
        }
        self.firstName = fullName.substringToIndex(spaceindex)
        self.lastName = fullName.substringFromIndex(spaceindex.successor())
    }
    
    • 引用类型

    类是引用类型。

    let person1 = Person(firstName: "Edward", lastName: "Newgate")
    let person2 = person1
    person2.firstName = "Steve"
    person2.lastName = "Jobs"
    person2.career = "CEO"
    
    //对 person2 修改时,person1 也改变了。(因为二者指向的是同一个对象)
    
    • 类的等价

    判断类的两个对象是否等价,判断的是其引用是否指向同一块内存。使用 === 表示,例如:

    person1 === person2 //false, 判断引用类型(比较的引用,是否指向同一块内存)
    person1 === person3 //true
    
    person1 !== person2 //true, 不等于,即不是同一块内存
    

    属性和方法

    • 计算属性

    计算属性:依赖其他属性而存在的属性。

    struct Point {
        var x = 0.0
        var y = 0.0
    }
    
    struct Size {
        var width = 0.0
        var height = 0.0
    }
    
    class Rectangle {
        var origin = Point()
        var size = Size()
        
        //计算属性
    //    var area:Double{
    //        return size.width * size.height
    //    }
        //也可以这样声明
        var area:Double{
            get{
                return size.width * size.height
            }
        }
        
        //getter, setter
        var center:Point { //必须为 var 类型,且显式声明类型
            //getter
            get {
                let centerX = origin.x + size.width/2
                let centerY = origin.y + size.height/2
                return Point(x: centerX, y: centerY)
            }
            //setter
    //        set(newCenter) {
    //            origin.x = newCenter.x - size.width/2
    //            origin.y = newCenter.y - size.height/2
    //        }
            set { //可以这么写,newValue 是默认值
                origin.x = newValue.x - size.width/2
                origin.y = newValue.y - size.height/2
            }
        }
    
        init(origin:Point, size:Size){
            self.origin = origin
            self.size = size
        }
    }
    
    • 类型属性

    类型属性,即类的属性,相当于静态变量,使用 static 关键字。例如:

    class Player {
        var name:String
        var score = 0 //个人总分
        static var highestScore = 0 //所有玩家最高分,类的属性 (static 关键字)
        
        init(name:String){
            self.name = name
        }
    }
    
    • 类型方法

    类型方法,即类的方法,相当于静态方法,使用 static 关键字。例如:

    class Matrix {
        var m:[[Int]] //二维数组
        var row:Int
        var col:Int
        
        ...
    
        //类方法,生成单位矩阵
        static func identityMatrix(n:Int) -> Matrix? {
            if n <= 0 {
                return nil
            }
            
            var arr2d:[[Int]] = []
            for i in 0..<n {
                var row = [Int](count:n, repeatedValue:0) //生成一行全为0的元素
                row[i] = 1
                arr2d.append(row) //添加到二维数组中
            }
            return Matrix(arr2d)
        }
    }
    
    • 属性观察器

    属性观察器可以监测一个属性,在其将要改变或改变后进行一些操作。示例代码如下:

    class LightBulb {
        static let maxCurrent = 30
    
        var current = 0 {
            //赋值前的逻辑
    //        willSet(newCurrent){ //新的值,可以省略不写,使用系统默认值 newValue
    //            print("new current is \(newCurrent)")
    //        }
            
            willSet{ //效果同前者
                print("new current is \(newValue)")
            }
            
            //赋值完成后做的事情
            didSet(oldCurrent){ //oldCurrent 表示原来的值,可以省略不写,使用系统默认值 oldValue
                if current == LightBulb.maxCurrent {
                    print("The current value get to the maximum point.")
                }
                else if current > LightBulb.maxCurrent {
                    print("current too hight, falling back to previous one.")
                    current = oldCurrent
                }
                print("The current is \(current)")
            }
        }
    }
    
    • 延迟属性

    延迟属性,lazy 关键字。一个属性加载一次后保存其结果,避免每次都重新加载。示例代码:

    class Book {
        let name:String
        //延迟属性
        lazy var content:String? = {
            return nil
        }()
        
        init(name:String){
            self.name = name
        }
    }
    
    • 访问控制

    public: 可以被模块外访问。
    internal: 可以被本模块访问。
    private: 可以被本文件访问。

    继承和构造函数

    • 继承

    示例代码(这里 Guldan 类继承自 Hero 类):

    public class Hero {
        var name:String
        var life:Int = 100
        
        public init(name:String){
            self.name = name
        }
    }
    
    final class Guldan: Hero {
        
    }
    

    注:若不想一个类被继承,可在前面添加 final 关键字。

    • 重写

    重写/覆盖 (关键字override),就是子类重写父类的属性和方法。示例代码:

    //父类
    public class Hero {
        var name:String
        var life:Int = 100
        
        var description:String{
            return "I'm \(name)."
        }
        
        func beAttacked(attack:Int) {
            life -= 10
        }
        
        public init(name:String){
            self.name = name
        }
    }
    
    //子类,使用了 final 关键字, 该类不可被继承
    final class Guldan: Hero {
        //属性重写 (override 关键字)
        override var description: String{
            return "Your soul belongs to me!"
        }
        
        //构造方法重写
        override init(name: String) { //构造方法重载
            self.group = ""
            print("my name is \(name)")
            super.init(name: name)
        }
        
        //方法重写
        override func beAttacked(attack: Int) {
            life -= 15
        }
    }
    

    注:若不想方法被重写,可以在方法前使用 final 关键字。

    • 便利构造函数和指定构造函数

    便利构造函数(关键字 convenience),是在构造函数中调用了其他的构造函数。而其他的构造函数则成为指定的 (designated) 构造函数。示例代码:

    //父类
    public class Hero {
    
        ...
            
        //指定的构造函数
        public init(name:String){
            self.name = name
        }
    }
    
    //子类
    final class Guldan: Hero {
        
        //构造函数重写
        override init(name: String) { //构造方法重载
            self.group = ""
            print("my name is \(name)")
            super.init(name: name)
        }
        
        //便利的构造函数
        convenience init(firstName:String, lastName:String){
            self.init(name:firstName + " " + lastName) //调用指定的初始化函数
        }
        
        ...
    }
    
    • 构造函数的继承

    子类构造函数的继承原则:

    1. 如果子类没有实现任何父类的指定构造函数,则自动继承父类所有的指定构造函数。
    2. 如果子类实现了父类所有的指定构造函数,则自动继承父类所有的便利构造函数。

    其他

    • 文档和注释

    三条斜杠 /// 可以生成文档注释;
    使用 MARK, TODOFIXME 可以给代码添加一些提醒,示例如下:

    //MARK: - init 方法
    //TODO: 有待添加一些功能
    //FIXME: 有些不影响程序运行的小问题,有待以后调整
    

    效果如图所示:

    效果图

    玩儿转Swift 2.0(第三季)

    相关文章

      网友评论

          本文标题:Swift 语法(三)

          本文链接:https://www.haomeiwen.com/subject/klewjttx.html