美文网首页Swift5.1语法学习
十一、 方法、下标、继承

十一、 方法、下标、继承

作者: 爱玩游戏的iOS菜鸟 | 来源:发表于2020-01-09 12:45 被阅读0次

    方法

    什么是方法?

    方法是关联了特定类型的函数
    类、结构体以及枚举都能定义实例方法,同时也都能定义类型方法

    • 实例方法 通过实例访问
    • 类型方法 通过类型调用,用static或者class关键字定义

    在OC中,类是唯一能定义方法的类型;
    而Swift中,枚举、结构体以及类都拥有强大的灵活性在创建的类型中定义方法

    方法的语法(使用方法需要注意)
    1. 方法中使用self
    class Car {
        var wheels = 4
        
        static var count = 0
        init(wheels:Int) {
            Car.count += 1
            self.wheels = wheels
        }
        static func getCount() -> Int {
            self.count
        }
        func getWheels() -> Int {
            self.wheels
        }
    }
    
    var hyundai = Car.init(wheels:4)
    print("HYUNDAI have \(hyundai.getWheels()) wheels")//HYUNDAI have 4 wheels
    
    var haval = Car.init(wheels:4)
    print("HAVAL have \(haval.getWheels()) wheels")//HAVAL have 4 wheels
    
    print("you have \(Car.getCount()) cars")//you have 2 cars
    

    在类型方法static func getCount()中 count 等价于 self.count 、 Car.self.count 、Car.count

    在上面的代码中,实例方法以及类型方法都使用了self,但是self在实例方法中代表实例对象,在类型方法中代表类型

    1. 使用mutating修饰func
    struct Point {
        var x = 0.0,y = 0.0
        mutating func moveBy(deltaX:Double,deltaY:Double) {
            x += deltaX
            y += deltaY
        }
    }
    
    enum StateSwitch {
        case low,middle,high
        mutating func next() {
            switch self {
            case .low:
                self = .middle
            case .middle:
                self = .high
            case .high:
                self = .low
            }
        }
    }
    

    结构体和枚举是值类型,默认情况下,值类型的属性是不能被自身的实例方法修改
    func关键字前加mutating可以允许这种修改行为

    1. 使用@discardableResult修饰func
    struct Point {
        var x = 0.0,y = 0.0
        @discardableResult mutating func moveBy(deltaX:Double,deltaY:Double) -> (Double, Double){
            x += deltaX
            y += deltaY
            return (x, y)
        }
    }
    
    var p = Point()
    p.moveBy(deltaX: 10, deltaY: 20)
    

    如果该函数不加添加@ discardableResult,调用后返回值未被使用,则会报警告


    在func前面添加@ discardableResult可以消除警告

    下标

    1. 什么是下标?下标的用途是什么?
    • 下标:枚举、类和结构体可以定义下标,作为访问集合、列表或者序列的成员元素快捷方式
    • 用途:可使用下标通过索引值来设置或访问
    1. 下标的语法
    • 使用subscript可以给任意类型(枚举、类、结构体)增加下标功能
    • subscript的语法类似实例方法计算属性,本质就是方法(函数)
    class Point {
        var x = 0.0, y = 0.0
        subscript (index:Int) ->Double{
            set{
                switch index{
                case 0:
                    x = newValue
                case 1:
                    y = newValue
                default :
                    break;
                }
            }
            
            get{
                switch index{
                case 0:
                    return x
                case 1:
                    return y
                default :
                    return 0
                }
            }
        }
    }
    
    var p = Point()
    p[0] = 20 //set  case:0  x= 20
    p[1] = 30 //set  case:1  y= 30
    
    print(p[0]) //get  case:0  return x
    print(p[1]) //get  case:1  return y
    

    要点
    subscript中定义的返回值类型决定了get方法中的返回值类型,决定了set方法中newValue的类型
    subscriot可以接受任意数量任意类型的参数

    • subscript可以没有set方法,但必须要有get方法
    class Point {
        var x = 0.0, y = 0.0
        subscript (index:Int) ->Double{
            //get{
                switch index{
                case 0:
                    return x
                case 1:
                    return y
                default :
                    return 0
                }
            //}
        }
    }
    
    var p = Point()
    
    //p[0] = 20 无法赋值
    //p[1] = 30
    
    print(p[0]) //get  case:0  return x
    print(p[1]) //get  case:1  return y
    
    

    要点
    类似计算属性,也可以省略get

    • 下标语法可以设置参数标签
    class Point {
        var x = 0.0, y = 0.0
        subscript (index i:Int) ->Double{
            get{
                switch I{
                case 0:
                    return x
                case 1:
                    return y
                default :
                    return 0
                }
            }
        }
    }
    
    var p = Point()
    
    print(p[index: 0])
    print(p[index: 1])
    

    注意 添加参数标签后,参数标签不可省略

    省略参数标签报错!
    • 下标语法可以是类方法
    class Sum {
        static subscript(num1 v1:Int,num2 v2:Int) -> Int{
            return v1 + v2
        }
    }
    
    print(Sum[num1: 10, num2: 20])//30
    
    1. 结构体、类作为返回值对比
     class Point {
        var x = 10,y = 20
    }
    
    class PointManager {
        var point = Point();
        subscript(index:Int) ->Point{
            get{
                point
            }
        }
    }
    
    var pM = PointManager()
    pM[0].x = 100
    pM[1].y = 200
    print(pM[0].x,pM[1].y,pM[10])
    
    struct Point {
        var x = 10,y = 20
    }
    
    class PointManager {
        var point = Point();
        subscript(index:Int) ->Point{
            set {
                point = newValue
            }
            get{
                point
            }
        }
    }
    
    var pM = PointManager()
    pM[0].x = 100
    pM[1].y = 200
    print(pM[0].x,pM[1].y,pM[10])
    

    上述的代码,Point一个是class(引用类型),一个是结构体(值类型),后者需要写set方法,不写就会报错,为什么会这样呢?

    Point为结构体的时候,不写set方法会报错

    注意
    pM[0].x = 100 在Point为class时,调用get方法,返回point对象,可以直接改
    pM[0].x = 100 在Point为struct时,调用set方法,实际相当于pm[0] = Point(x:100, y:20) x为新值,y不变

    最后添加一个scbscript接受多个参数的下标举例

    class Gird {
        var data = [[2,10,50],
                    [50,46,71],
                    [19,91,32]]
        subscript(section:Int,row:Int) -> Int{
            set{
                guard section >= 0 && section < 3 && row >= 0 && row < 3 else {
                    return
                }
                data[section][row] = newValue
            }
            get{
                guard section >= 0 && section < 3 && row >= 0 && row < 3 else {
                    return 0
                }
                return data[section][row]
            }
        }
    }
    

    继承

    • 值类型(枚举、结构体)不支持继承,只有支持继承
    • 任何不从另一个类继承的类,都称为基类 ( Swift没有像OC、Java规定:最终都要继承自某类)
    • 子类是基于现有类创建新类的行为。子类可以重写父类的下标、方法、属性,重写必须加上override关键字
    1. 继承-内存结构如何存储的
    class Animal {
        var age = 0
    }
    
    class Dog: Animal {
        var weight = 0
    }
    
    
    class ErHa: Dog {
        var iq = 0
    }
    
    var a = Animal()
    a.age = 10
    print(Mems.size(ofRef: a))//32  16+8 (age) 
    print(Mems.memStr(ofRef: a))
    /*
     0x0000000100008418 0x0000000200000002
     0x000000000000000a 0x0002000100500220
     */
    
    var d = Dog()
    d.weight = 11
    print(Mems.size(ofRef: d))//32  16+8(age) +8(weight)
    print(Mems.memStr(ofRef: d))
    /*
     0x00000001000084c8 0x0000000200000002
     0x0000000000000000 0x000000000000000b
    */
    
    
    var e = ErHa()
    e.iq = 12
    print(Mems.size(ofRef: e))//48  16+8(age) +8(weight)+8(e.iq)
    print(Mems.memStr(ofRef: e))
    /*
     0x0000000100008598 0x0000000200000002
     0x0000000000000000 0x0000000000000000
     0x000000000000000c 0x0003000000000000
    */
    

    上述代码结论证明 子类的实例对象对将父类的存储属性作为自己的属性存储,并存放在靠前的字节

    1. 继承-重写
      1. 重写实例方法、下标
    class Animal {
        func speak() {
            print("Animal speak")
        }
        subscript (index:Int) -> Int{
            return index
        }
    }
    
    var anim : Animal
    anim = Animal()
    
    anim.speak()//Animal speak
    print(anim[6])//6
    
    class Cat: Animal {
        override func speak() {
            super.speak()
            print("Cat speak")
        }
        
        override subscript(index: Int) -> Int {
            return super[index]+1
        }
    }
    
    anim = Cat()//多态 父类指针指向子类对象
    anim.speak()
    //Animal speak
    //Cat speak
     print(anim[6])//7
    
    1. 重写类型方法、下标
    class Animal {
        class func speak() {
            print("Animal speak")
        }
        class subscript (index:Int) -> Int{
            return index
        }
    }
    
    Animal.speak()//Animal speak
    print(Animal[6])//6
    
    class Cat: Animal {
        override class func speak() {
            super.speak()
            print("Cat speak")
        }
    
        override class subscript(index: Int) -> Int {
            return super[index]+1
        }
    }
    
    Cat.speak()
    //Animal speak
    //Cat speak
     print(Cat[6])//7
    

    class修饰的类型方法、下标,允许被子类重写
    static修饰的类型方法、下标,不允许被子类重写

    static修饰的类型方法 下标 不允许被子类重写
    1. 重写属性
    • 重写实例属性
    class Circle {
        var radius = 0
        var diameter :Int{
            set{
                print("setParentDiameters")
                self.radius = newValue/2
            }
            get{
                print("getParentDiameters")
                return self.radius*2
            }
        }
    }
    
    class SubCircle: Circle {
        override var radius: Int {
            set{
                super.radius = newValue
                print("setChildRadius")
            }
            get{
                print("getChildRadius")
                return super.radius
            }
        }
        override var diameter :Int{
            set{
                print("setChildDiameter")
                super.diameter = newValue
            }
            get{
                print("getChildDiameter")
                return super.diameter
            }
        }
    }
    
    var dog = SubCircle()
    print("————下面打印dog.radius的Set——————————")
    dog.radius = 10
    print("————下面打印dog.radius的Get——————————")
    print(dog.radius)
    
    print("————下面打印dog.diameter的Set——————————")
    dog.diameter = 10
    print("————下面打印dog.diameter的Get——————————")
    print(dog.diameter)
    
    打印结果,和预想的相同吗?

    【要点】
    1.子类可以将父类的(存储、计算)属性重写为计算属性
    2.子类不可以将父类的计算属性重写为存储属性
    3.只能重写var属性,不能重写let属性

        class Circle {
        var radius = 0
        var diameter :Int{//只有get方法
                print("getParentDiameters")
                return self.radius*2
        }
    }
    
    class SubCircle: Circle {
        override var radius: Int {
            set{
                super.radius = newValue
                print("setChildRadius")
            }
            get{
                print("getChildRadius")
                return super.radius
            }
        }
        override var diameter :Int{
            //添加set方法
            set{
                print("setChildDiameter")
                super.radius = newValue / 2
            }
            get{
                print("getChildDiameter")
                return super.diameter
            }
        }
    }
    
    var dog = SubCircle()
    print("————下面打印dog.radius的Set——————————")
    dog.radius = 10
    print("————下面打印dog.radius的Get——————————")
    print(dog.radius)
    
    print("————下面打印dog.diameter的Set——————————")
    dog.diameter = 10
    print("————下面打印dog.diameter的Get——————————")
    print(dog.diameter)
    

    【要点】
    子类重写后的属性权限 不能小于父类属性的权限
    比如:
    如果父类的属性时只读 子类可以是只读或者读写
    如果父类的属性时读写 子类必须要是读写

    • 重写类型属性
    class Circle {
        static var radius = 0
        class var diameter :Int{
            set{
                print("setParentDiameters")
                radius = newValue / 2
            }
            get{
                print("getParentDiameters")
                return self.radius*2
            }
        }
    }
    
    class SubCircle: Circle {
        override static var diameter :Int{
            set{
                print("setChildDiameter")
                super.diameter = newValue
            }
            get{
                print("getChildDiameter")
                return super.diameter
            }
        }
    }
    
    SubCircle.radius = 10
    print(SubCircle.radius)
    
    print("————下面打印SubCircle.diameter的Set——————————")
    SubCircle.diameter = 10
    print("————下面打印SubCircle.diameter的Get——————————")
    print(SubCircle.diameter)
    
    打印结果

    class修饰的计算类型属性,可以被子类重写
    static修饰的(存储、计算)类型属性,不可以被子类重写

    1. 属性观察器
      可以在子类中为父类(实例 、类型属性)属性(除了只读计算属性、let属性)增加属性观察器
    class Circle {
        var radius = 1{
            willSet{
                print("willSetParentRadius \(newValue)")
            }
            didSet{
                print("didSetParentRadius \(oldValue) ,\(radius)")
            }
        }
        var diameter :Int{
            set{
                print("setParentDiameters")
                self.radius = newValue/2
            }
            get{
                print("getParentDiameters")
                return self.radius*2
            }
        }
    }
    
    class SubCircle: Circle {
        override var radius: Int {
            willSet{
                print("willSetChildRadius \(newValue)")
            }
            didSet{
                print("didSetChildRadius \(oldValue) ,\(radius)")
            }
        }
        override var diameter :Int{
            willSet{
                print("willSetChildDiameter \(newValue)")
            }
            didSet{
                print("didSetChildDiameter \(oldValue) ,\(diameter)")
            }
        }
    }
    
    var dog = SubCircle()
    print("————下面打印dog.radius的Set——————————")
    dog.radius = 10
    print("————下面打印dog.radius的Get——————————")
    print(dog.radius)
    
    print("————下面打印dog.diameter的Set——————————")
    dog.diameter = 10
    print("————下面打印dog.diameter的Get——————————")
    print(dog.diameter)
    
    打印结果 和你预想的一样吗?

    注意 这里打印dog.diameter的Set时有一点特殊
    这里先打印了getParentDiameters ,就是需要先拿到diameter的值,调用了父类的get方法

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

    多态 下一章节单独 讲述

    Swift学习日记11

    相关文章

      网友评论

        本文标题:十一、 方法、下标、继承

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