Swift_学习笔记_继承

来源:互联网 发布:淘宝商家怎么交保证金 编辑:程序博客网 时间:2024/06/05 02:00
  • 继承是面向对象编程语言的最主要特征之一
  • Swift只有单继承
  • 继承是类与结构体和枚举等类型最重要的区别
  • Swift可以重写父类的方法和属性

重写

  • 对属性或者方法进行重写时,需要关键字:override
// 重写hash方法class IOS:NSObject{    override class func hash() ->Int {        return "hello".hash    }}
  • 不仅仅可以重写父类的存储属性,也可以重写父类的计算属性。
  • 属性中如果有setter方法,则必须同时提供getter方法。
  • 不可以将父类中的读写属性,重写成只读属性,但是可以将父类的只读重写成读写属性。
  • 可以在子类中为父类的属性添加属性观察器,可以称之为重写属性观察器。
  • 如果父类的属性为只读,则不可以重写属性观察器。
  • 重写父类的setter方法与属性的观察器方法(willSet/didSet)是相互冲突的。

super关键字

  • 重写父类的方法,并不一定全部推翻父类的方法实现,这个时候就需要利用super关键字,来调用父类的方法
override func viewDidLoad() {        super.viewDidLoad()}

final关键字

  • Swift语言中特有的关键字。
  • 阻止父类中的某个属性或者方法被子类重写,即被final修饰的属性或者方法,将不能被子类重写。

构造方法

  • 构造方法是在创建对象时被隐式调用的方法
  • 目的:初始化刚刚被构造出来的对象内存

构造方法的特征

  • 构造方法的名称为init
  • 同一类型可以有多个构造方法,但是构造方法的参数个数或者类型不能一样
  • 构造方法无返回值
  • 构造方法的引用是通过类型隐式调用的

构造方法的参数名称

  • 构造方法默认将内部参数名,作为外部参数名。
  • 通过在构造方法,参数名称前添加 “_”,可以将构造方法的外部参数名隐藏
class Human{    var name:String    var  age:Int    init(_ name:String,_ age:Int) {        self.name = name        self.age = age    }   }var human = Human("aaa", 30)

属性的缺省值

含有缺省值属性的构造方法

  • 属性的初始值即缺省值

缺省构造方法

  • 如果没有任何自定义的构造方法,系统将默认提供一个无任何参数的构造方法。
  • 如果已经自定义构造方法,则系统提供的自动失效。

结构体的构造方法

  • 结构体,系统会自动提供一个全参构造方法
  • 如果自己声明了一个构造方法,则系统提供的全参构造方法自动失效
struct Size{    var width:Double    var height:Double}var size = Size(width: 20, height: 20)

枚举类型的构造方法

  • 枚举不能通过类型的隠式解析调用构造方法,只有定义了构造方法,才可以像类那样构造对象
enum Color{    case Red    case Blue    case White    init() {        self = .Red    }    init(color:Color) {        self = color    }}var c1 = Color.Bluevar c2 = Color()var c3 = Color(color: .Blue)print(c1)print(c2)print(c3)// 如果不声明构造方法,则c2/c3均不可用

值类型的构造方法代理

  • 值类型特指结构体或者枚举
  • 构造方法代理是值如果某个值类型同时又多个构造方法,那么构造方法之间可以通过self 关键字相互调用
  • 当调用某个构造方法时,一定要通过self调用
  • 不要再调用构造方法之前做任何存储属性的访问操作,包括读属性和属性赋值操作
struct Size{    var width:Double    var height:Double    init(width:Double,height:Double) {        self.width = width        self.height = height    }    init() {//        height = 10.0 // 错误//        width = 20 // 错误        self.init(width: 10, height: 20)        // 此语句在属性赋值之后,没有任何问题        if width > 10 {            width = 10        }    }}

可选类型属性与构造方法

  • 某个属性为可选类型,声明构造方法时,可以不对此值进行初始化
class Human{    var name:String    var  age:Int    var bankCardNumber:Double?    // 对于可选类型,默认值为nil    init(name:String, age:Int) {        self.name = name        self.age = age    }}var man = Human(name: "WANG", age: 20)print(man.name)print(man.bankCardNumber)

常量属性与构造方法

  • 对于常量属性,可以在构造方法内不对其赋值
  • 常量属性只可以在定义的同时进行初始化,在构造方法内不可以对其再进行修改
class Human{    var name:String    var  age:Int    let ID:Int = 411111111    var bankCardNumber:Double?    // 对于可选类型,默认值为nil    init(name:String, age:Int,ID:Int) {        self.name = name        self.age = age        self.ID = ID // 不可以再次赋值    }    init(name:String, age:Int) {        self.name = name        self.age = age    }}var man1 = Human(name: "WANG", age: 20)var man2 = Human(name: "LI", age: 23, ID: 111111)

通过闭包或者函数设置属性的缺省值(默认值)

// 扑克牌种类enum PokerKind:String{    case heart = "♥"    case diamond = "♦"    case club = "♣"    case spade = "♠"}// 扑克牌号码enum PokerNumber:String{    case NA = " A"    case N2 = " 2"    case N3 = " 3"    case N4 = " 4"    case N5 = " 5"    case N6 = " 6"    case N7 = " 7"    case N8 = " 8"    case N9 = " 9"    case N10 = " 10"    case NJ = " J"    case NQ = " Q"    case NK = " K"}// 扑克牌struct PokerCard{    var kind:PokerKind    var number:PokerNumber    var name:String{        return "\(kind)\(number)"    }}// 扑克牌选手class PokerPalyer{    // 通过类方法 初始化    static var pokerCardArray:[PokerCard] = PokerPalyer.setupCards()    // 通过闭包来随机产生13张牌    var cards:[PokerCard] = {        var temp = [PokerCard]()        for i in 0..<13 {            var s = Int(arc4random()) % pokerCardArray.count            temp.append(pokerCardArray.remove(at: s))        }        return temp    }()    /* 闭包的声明:    *  {(参数:参数类型)->(返回值) in 执行语句}    *  注意此处的"()",如果没有则是声明一个闭包,并将闭包赋值给cards,因为有"()",所以执行了闭包。    */    // 类方法,初始化52张牌    static func setupCards() -> [PokerCard]{        var temp = [PokerCard]()        var k:[PokerKind] = [.heart,.diamond,.spade,.club]        var n:[PokerNumber] = [.NA,.N2,.N3,.N4,.N5,.N6,.N7,.N8,.N9,.N10,.NJ,.NQ,.NK]        for i in 0..<3 {            for j in 0..<13 {                let card:PokerCard = PokerCard(kind: k[i], number: n[j])                temp.append(card)            }        }        return temp    }}// 构造四个扑克牌选手var palyer1 = PokerPalyer()var palyer2 = PokerPalyer()var palyer3 = PokerPalyer()var palyer4 = PokerPalyer()var players = [palyer1,palyer2,palyer3,palyer4];// 遍历每个选手的扑克牌for player in players {    var string:String = ""    for card in player.cards {        string.append("\(card)")    }    print("选手:\(palyer1)\n扑克有:\(string)")}

派生类(子类)的构造方法

便利构造方法和指定构造方法

  • 只要某个指定构造方法,利用self调用了其它的指定构造方法,则此构造方法必须用convenience进行修饰,使其变成便利构造方法。
class Human{    var name:String = "OK"    var  age:Int = 100    var bankCardNumber:Double?    // 父类 指定构造器    init(name:String, age:Int) {        self.name = name        self.age = age    }    // 父类 便利构造器    convenience init (){        // 同时在self.init之前不可以进行任何存储值得访问        self.init(name: "Li", age: 30)    }}
  • 一个类可以有多个指定构造器和便利构造器。
  • 一个类可以没有便利构造器,但是必须有指定构造器。因为,便利构造器总是调用本类中的指定构造器完成初始化任务。

给派生类编写构造方法

  • 派生类中继承了父类的存储属性,也有自己的存储属性,因此派生类的构造方法,需要负责本类以及父类中的存储属性的初始化。
  • 对父类的存储属性进行初始化可以采用以下两种方式:
    1. 直接对父类的存储属性进行赋值
    2. 利用super关键字调用父类的构造方法(主要利用这种)
  • 派生类构造方法需要以下三步:
    1. 对本类的存储属性进行赋值;
    2. 利用super关键字对父类存储属性进行赋值;
    3. 进行其他额外的处理。

构造方法的重写

  • 重写的构造方法需要关键字 override修饰
  • 父类中的便利构造器,不可以被子类重写
  • 根据构造方法的调用规则,便利构造器只能在本类中被调用而无法被派生类以任何的方式来调用。因此便利构造器不可以被重写。
class Human{    var name:String    var  age:Int    var bankCardNumber:Double?    init(name:String, age:Int) {        self.name = name        self.age = age    }    convenience init (){        self.init(name: "Li", age: 30)    }}class Man:Human{    init() {        // 报错    }}
  • 父类的指定构造器,可以在子类中被重写成便利构造器,需要关键字 override convenience同时使用
class Human{    var name:String    var  age:Int    var bankCardNumber:Double?    // 父类的指定构造器    init(name:String, age:Int) {        self.name = name        self.age = age    }    convenience init (){        self.init(name: "Li", age: 30)    }}class Man:Human{    // 将父类的指定构造器重写成便利构造器    override convenience init(name: String, age: Int) {        self.init(name: name, age: age)    }}
  • 由于便利构造器在子类中不能被重写,但是可以定义一个跟父类同名的构造方法,由于不是重写,不要关键字 override
class Human{    var name:String    var  age:Int    var bankCardNumber:Double?    init(name:String, age:Int) {        self.name = name        self.age = age    }    convenience init (){        self.init(name: "Li", age: 30)    }}class Man:Human{  // 跟父类相同的构造方法,但不是重写,故不用添加override关键字    convenience init(){        self.init(name: "WANG", age: 22)    }}var man = Man()print(man.name) // WANG

构造方法的自动继承

  • 派生类(即子类)没有引入任何存储属性或者引入的存储属性都有缺省值(即有默认初始值),并且此时没有自定义任何的构造方法,这时候将会自动继承父类中所有的指定和便利构造器。
class Human{    var name:String = "OK"    var  age:Int = 100    var bankCardNumber:Double?    // 父类 指定构造器1    init(name:String, age:Int) {        self.name = name        self.age = age    }    // 父类 指定构造器2    init(name:String,age:Int,bankCardNumber:Double) {        self.name = name        self.age = age        self.bankCardNumber = bankCardNumber    }    // 父类 便利构造器    convenience init (){        self.init(name: "Li", age: 30)    }}class Man:Human{    // 引入的存储属性都有缺省值    //    var ID = 123456}var man = Man()print(man.name)
  • 派生类重写了父类所有的指定构造方法,也将自动继承父类所有的便利构造方法。
  • 派生类只重写了部分父类的指定构造方法,将不再继承父类中的便利构造方法。
class Human{    var name:String    var  age:Int    var bankCardNumber:Double?    // 父类 指定构造器1    init(name:String, age:Int) {        self.name = name        self.age = age    }    // 父类 指定构造器2    init(name:String,age:Int,bankCardNumber:Double) {        self.name = name        self.age = age        self.bankCardNumber = bankCardNumber    }    // 父类 便利构造器    convenience init (){        self.init(name: "Li", age: 30)    }}class Man:Human{    // 重写 指定构造器1    override init(name: String, age: Int) {        super.init(name: name, age: age)        print("aaaaa")    }}var man = Man() // 报错,由于子类只重写了父类指定构造器1,所以子类没有继承父类的便利构造器

必须构造方法

  • 必须构造方法关键字:required
  • 如果子类重写父类中已被required 修饰的构造方法,不需要添加关键字 override
  • 子类重写的父类中已被required 修饰的构造方法,需要在构造方法前添加required关键字
class Human{    var name:String    var  age:Int    required init(_ name:String,_ age:Int) {        self.name = name        self.age = age    }}class Man:Human{    // 重写父类 必须构造方法    required init(_ name:String,_ age:Int) {        super.init(name, age)    }}

析构方法

  • 析构方法是在对象被释放前隐式调用的方法
  • 目的:在对象内存被系统回收之前做一些收尾工作
  • Swift内存管理模式是ARC,因此析构方法并没有MRC那么重要
  • 析构方法的主要工作不是内存管理(主要是内存释放工作),而是一些其他的操作

析构方法语法

  • 析构方法使用关键字:deinit
deinit {// 无参无返回值}

析构方法的自动继承

  • 父类的析构方法会被子类自动继承,子类的析构方法执行完之后,会立即执行父类的析构方法。
class SomeClass{    deinit {        print("SmomeClass deinit!")    }}class SubOfClass:SomeClass{    deinit {        print("SubOfClass deinit!")    }}// 利用弱引用,使得对象在创建之后可以被立马释放掉weak var weakRef = SubOfClass()// SubOfClass deinit!// SmomeClass deinit!

类扩展

  • 类扩展不能扩展增加类的存储属性

类扩展的语法

  • 需要使用关键字:extension
class SomeClass{    // 原有的功能}extension SomeClass{   // 添加新的功能}
  • 可以让已经存在的类遵守某些新的协议
protocol SomeClassDelegate{    // 协议中的方法列表}extension SomeClass:SomeClassDelegate{    // 添加新的功能}

扩展运算属性

  • 对于运算属性,Swift支持扩展
class Person{    var height:Double?    var sex:Bool = true}extension Person{    var standardWeight:Double{        let e = self.sex ? 22.0 :20.0        return height! * height! * e    }}var person = Person()person.height = 30print(person.standardWeight) //19800.0

扩展构造方法

  • 扩展的构造器只能是便捷构造方法,普通的构造方法需要在原始的类中声明。
  • 扩展构造方法需要关键字:convenience
class Person{    var height:Double?    var sex:Bool = true}extension Person{    convenience init(height:Double) {        self.init()        self.height = height    }}var person = Person(height: 12.3)

扩展普通方法

  • 扩展普通的方法。只需要声明即可,但是不可以和原始类中的方法名相同

扩展下标

  • 在扩展内添加subscript下标
class Carriage{    var name:String    init(name:String) {        self.name = name    }}class Train{    var carriageArray:[Carriage] = []    init(number:Int) {        for i in 1...number {            carriageArray.append(Carriage(name: "车厢:\(i)"))        }    }}extension Train{    subscript (index:Int)->Carriage{        return carriageArray[index]    }}let train = Train(number: 10)print(train[1].name) // 车厢:2
原创粉丝点击