Swift第五章 方法、下标、继承

方法(Method)

  1. 枚举、结构体、类都可以定义实例方法、类型方法
  2. 实例方法(Instance Method):通过实例对象调用
  3. 类型方法(Type Method):通过类型调用,用static或者class关键字定义

     class Car {
         static var cout = 0
         init() {
             Car.cout += 1
         }
         //类型方法
         static func getCount() -> Int { cout }
     }
     let c0 = Car()
     let c1 = Car()
     let c2 = Car()
     print(Car.getCount()) // 3
    
  4. self
    1. 在实例方法中代表实例对象
    2. 在类型方法中代表类型
  5. 在类型方法static func getCount中
    1. cout等价于self.cout、Car.self.cout、Car.cout

mutating

  1. 结构体和枚举是值类型,默认情况下,值类型的属性不能被自身的实例方法修改
  2. 在func关键字前加mutating可以允许这种修改行为
struct Point {
    var x = 0.0, y = 0.0
    //如果不加mutating,会报错,因为x,y为实例值类型的属性;如果当前是类class,可以修改。
    mutating func moveBy(deltaX: Double, deltaY: Double) {
        x += deltaX
        y += deltaY
        // self = Point(x: x + deltaX, y: y + deltaY)
    }
}

enum StateSwitch {
    case low, middle, high
    //每次调用next多会改变枚举值
    //如果不加mutating,会报错,因为low,middle,high为实例值类型的属性
    mutating func next() {
        switch self {
            case .low:
                self = .middle
            case .middle:
                self = .high
            case .high:
                self = .low
        }
    }
}

@discardableResult

  1. 在func前面加个@discardableResult,可以消除:函数调用后返回值未被使用的警告⚠

     struct Point {
         var x = 0.0, y = 0.0
         @discardableResult mutating
         func moveX(deltaX: Double) -> Double {
             x += deltaX
             return x
         }
     }
     var p = Point()
     p.moveX(deltaX: 10)
        
     @discardableResult
     func get() -> Int {
         return 10
     }
     get()
    

下标(subscript)

  1. 使用subscript可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为:下标脚本
  2. subscript的语法类似于实例方法、计算属性,本质就是方法(函数)

     class Point {
         var x = 0.0, y = 0.0
         //定义一个下标
         subscript(index: Int) -> Double {
             set {
                 if index == 0 {
                     x = newValue
                 } else if index == 1 {
                     y = newValue
                 }
             }
             get {
                 if index == 0 {
                     return x
                 } else if index == 1 {
                     return y
                 }
                 return 0
             }
         }
     }
        
        
     var p = Point()
     //调用下标,设置值
     p[0] = 11.1
     p[1] = 22.2
     print(p.x) // 11.1
     print(p.y) // 22.2
     //获取下标值
     print(p[0]) // 11.1
     print(p[1]) // 22.2
    
  3. subscript中定义的返回值类型决定了
    1. get方法的返回值类型
    2. set方法中newValue的类型
  4. subscript可以接受多个参数,并且类型任意

下标细节

  1. subscript可以没有set方法,但必须要有get方法

     class Point {
         var x = 0.0, y = 0.0
         subscript(index: Int) -> Double {
             get {
                 if index == 0 {
                     return x
                 } else if index == 1 {
                     return y
                 }
                 return 0
             }
         }
     }
    
  2. 如果只有get方法,可以省略get

     class Point {
         var x = 0.0, y = 0.0
         //get方法的省略
         subscript(index: Int) -> Double {
             if index == 0 {
                 return x
             } else if index == 1 {
                 return y
             }
             return 0
         }
     }
    
  3. 可以设置参数标签

     class Point {
         var x = 0.0, y = 0.0
         subscript(index i: Int) -> Double {
             if i == 0 {
                 return x
             } else if i == 1 {
                 return y
             }
             return 0
         }
     }
     var p = Point()
     p.y = 22.2
     print(p[index: 1]) // 22.2
    
  4. 下标可以是类型方法

     class Sum {
         //设置类方法下标
         static subscript(v1: Int, v2: Int) -> Int {
             return v1 + v2
         }
     }
     print(Sum[10, 20]) // 30
    

结构体、类作为返回值对比

//返回值为class
class Point {
    var x = 0, y = 0
}
class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        //返回值为class,则不用实现set方法,pm[0].x = 11不会报错
        get { point }
    }
}

//返回值为struct
struct Point {
    var x = 0, y = 0
}
class PointManager {
    var point = Point()
    subscript(index: Int) -> Point {
        //必须实现,否则无法赋值,pm[0].x = 11会报错
        set { point = newValue }
        get { point }
    }
}

var pm = PointManager()
pm[0].x = 11
pm[0].y = 22
// Point(x: 11, y: 22)
print(pm[0])
// Point(x: 11, y: 22)
print(pm.point)

接收多个参数的下标

class Grid {
    var data = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8]
    ]
    subscript(row: Int, column: Int) -> Int {
        set {
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return
            }
            data[row][column] = newValue
        }
        get {
            guard row >= 0 && row < 3 && column >= 0 && column < 3 else {
                return 0
            }
            return data[row][column]
        }
    }
}


var grid = Grid()
grid[0, 1] = 77
grid[1, 2] = 88
grid[2, 0] = 99
print(grid.data)

继承

  1. 值类型(枚举、结构体)不支持继承,只有类支持继承
  2. 没有父类的类,称为:基类
  3. Swift并没有像OC、Java那样的规定:任何类最终都要继承自某个基类
  4. 子类可以重写父类的下标、方法、属性,重写必须加上override关键字

重写实例方法、下标

  1. 举例

     class Animal {
         func speak() {
             print("Animal speak")
         }
         subscript(index: Int) -> Int {
             return index
         }
     }
        
     var anim: Animal
     anim = Animal()
     // Animal speak
     anim.speak()
     // 6
     print(anim[6])
        
        
     class Cat : Animal {
         override func speak() {
             super.speak()
             print("Cat speak")
         }
         override subscript(index: Int) -> Int {
             return super[index] + 1
         }
     }
    
     anim = Cat()
     // Animal speak
     // Cat speak
     anim.speak()
     // 7
     print(anim[6])
    

重写类型方法、下标

  1. 被class修饰的类型方法、下标,允许被子类重写
  2. 被static修饰的类型方法、下标,不允许被子类重写
class Animal {
    class func speak() {
        print("Animal speak")
    }
    class subscript(index: Int) -> Int {
        return index
    }
}
// Animal speak
Animal.speak()
// 6
print(Animal[6])


class Cat : Animal {
    override class func speak() {
        super.speak()
        print("Cat speak")
    }
    override class subscript(index: Int) -> Int {
        return super[index] + 1
    }
}
// Animal speak
// Cat speak
Cat.speak()
// 7
print(Cat[6])

重写属性

  1. 子类可以将父类的属性(存储、计算)重写为计算属性
  2. 子类不可以将父类属性重写为存储属性
  3. 只能重写var属性,不能重写let属性
  4. 重写时,属性名、类型要一致
  5. 子类重写后的属性权限 不能小于 父类属性的权限
    1. 如果父类属性是只读的,那么子类重写后的属性可以是只读的、也可以是可读写的
    2. 如果父类属性是可读写的,那么子类重写后的属性也必须是可读写的

重写实例属性

  1. 父类

     class Circle {
         var radius: Int = 0
         var diameter: Int {
             set {
                 print("Circle setDiameter")
                 //如果是子类实例调用,就是调用子类的set方法
                 radius = newValue / 2
             }
             get {
                 print("Circle getDiameter")
                 //如果是当前实例调用,就是拿到radius,如果是子类实例调用,就是调用子类的get方法
                 return radius * 2
             }
         }
     }
        
     var circle: Circle
     circle = Circle()
     circle.radius = 6
     // Circle getDiameter
     // 12
     print(circle.diameter)
     // Circle setDiameter
     circle.diameter = 20
     // 10
     print(circle.radius)
    
  2. 子类重写属性

     class SubCircle : Circle {
     //全部重写为计算属性
         override var radius: Int {
             set {
                 print("SubCircle setRadius")
                 super.radius = newValue > 0 ? newValue : 0
             }
             get {
                 print("SubCircle getRadius")
                 return super.radius
             }
         }
         override var diameter: Int {
             set {
                 print("SubCircle setDiameter")
                 super.diameter = newValue > 0 ? newValue : 0
             }
             get {
                 print("SubCircle getDiameter")
                 return super.diameter
             }
         }
     }
        
     //注意查看调用栈
     circle = SubCircle()
     // SubCircle setRadius
     circle.radius = 6
        
     // SubCircle getDiameter
     // Circle getDiameter
     // SubCircle getRadius ----此时父类Circle的return radius * 2,获取radius本质是调用当前子类的get方法
     // 12
     print(circle.diameter)
        
     // SubCircle setDiameter
     // Circle setDiameter
     // SubCircle setRadius---同理radius = newValue / 2,本质调用当前子类的set方法
     circle.diameter = 20
        
     // SubCircle getRadius
     // 10
     print(circle.radius)
    

重写类型属性

  1. 被class修饰的计算类型属性,可以被子类重写
  2. 被static修饰的类型属性(存储、计算),不可以被子类重写
class Circle {
    static var radius: Int = 0
    class var diameter: Int {
        set {
            print("Circle setDiameter")
            radius = newValue / 2
        }
        get {
            print("Circle getDiameter")
            return radius * 2
        }
    }
}


class SubCircle : Circle {
    override static var diameter: Int {
        set {
            print("SubCircle setDiameter")
            super.diameter = newValue > 0 ? newValue : 0
        }
        get {
            print("SubCircle getDiameter")
            return super.diameter
        }
    }
}


Circle.radius = 6
// Circle getDiameter
// 12

print(Circle.diameter)
// Circle setDiameter
Circle.diameter = 20
// 10
print(Circle.radius)
SubCircle.radius = 6

// SubCircle getDiameter
// Circle getDiameter
// 12

print(SubCircle.diameter)
// SubCircle setDiameter
// Circle setDiameter
SubCircle.diameter = 20
// 10
print(SubCircle.radius)

属性观察器

  1. 可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器

     class Circle {
         var radius: Int = 1
     }
     class SubCircle : Circle {
         override var radius: Int {
             willSet {
                 print("SubCircle willSetRadius", newValue)
             }
             didSet {
                 print("SubCircle didSetRadius", oldValue, radius)
             }
         }
     }
        
     var circle = SubCircle()
     // SubCircle willSetRadius 10
     // SubCircle didSetRadius 1 10
     circle.radius = 10
    
  2. 父类存在属性观察器,子类也可以重写

     class Circle {
         var radius: Int = 1 {
             willSet {
                 print("Circle willSetRadius", newValue)
             }
             didSet {
                 print("Circle didSetRadius", oldValue, radius)
             }
         }
     }
     class SubCircle : Circle {
         override var radius: Int {
             willSet {
                 print("SubCircle willSetRadius", newValue)
             }
             didSet {
                 print("SubCircle didSetRadius", oldValue, radius)
             }
         }
     }
        
        
     var circle = SubCircle()
     // SubCircle willSetRadius 10
     // Circle willSetRadius 10
     // Circle didSetRadius 1 10
     // SubCircle didSetRadius 1 10
     circle.radius = 10
    
  3. 父类如果是计算属性,子类可以添加属性观察器

     class Circle {
         var radius: Int {
             set {
                 print("Circle setRadius", newValue)
             }
             get {
                 print("Circle getRadius")
                 return 20
             }
         }
     }
     class SubCircle : Circle {
         override var radius: Int {
             willSet {
                 print("SubCircle willSetRadius", newValue)
             }
             didSet {
                 print("SubCircle didSetRadius", oldValue, radius)
             }
         }
     }
        
     var circle = SubCircle()
     // Circle getRadius
     // SubCircle willSetRadius 10
     // Circle setRadius 10
     // Circle getRadius
     // SubCircle didSetRadius 20 20
     circle.radius = 1
    
  4. 父类是类型计算属性

     class Circle {
         class var radius: Int {
             set {
                 print("Circle setRadius", newValue)
             }
             get {
                 print("Circle getRadius")
                 return 20
             }
         }
     }
        
     class SubCircle : Circle {
         override static var radius: Int {
             willSet {
                 print("SubCircle willSetRadius", newValue)
             }
             didSet {
                 print("SubCircle didSetRadius", oldValue, radius)
             }
         }
     }
        
     // Circle getRadius
     // SubCircle willSetRadius 10
     // Circle setRadius 10
     // Circle getRadius
     // SubCircle didSetRadius 20 20
     SubCircle.radius = 10
    

final

  1. 被final修饰的方法、下标、属性,禁止被重写
  2. 被final修饰的类,禁止被继承
Table of Contents