Swift4 类与继承, 类型转换, 类型判断

创建: 2018/03/05

完成: 2018/03/07

更新: 2018/03/09 完善标题 [Swift4 类与继承, 类型转换] -> [Swift4 类与继承, 类型转换与判断]

                         补充指定final的属性/方法无法被子类重载

更新: 2018/03/30 修改标题 [Swift4 类与继承, 类型转换与判断] -> [Swift4 类与继承, 类型转换, 类型判断]

【任务表】TODO

 

类定义
 类的概要

 

class 型名: 父类, 采用的协议 {
    变量/常量定义 // var/let
    构造函数定义
    方法定义
    其他定义  // 型, 属性, 索引subscript
}

class NullClass: ClassNameAchievable, CustomStringConvertible {
    var className: String {
        return "NullClass"
    }
    var description: String {
        return self.className
    }
    func printName() {
        print("printName: \(self)")
    }
}

 ● 只能继承一个父类

 ● 顺序随意

 ● 类的实例与闭包的实例是参照型

 ● 没有全项目构造函数, 不定义构造函数则只有默认构造函数(必须索所有属性都有初始值)

 ● 改变自身属性的函数不需要mutating

 类实例是参照型, 改变自身是指改变自身所指。let的不能改变自身所指

 构造体是值型, 改变自身属性也是改变自身

 类的继承

 ● 只能继承一个父类

 ● 被继承的叫父类(super class), 继承的叫子类(sub class), 没有继承其他类的叫基类(base class)

   注: C++里父类叫做基类(基底类)(base class)

 

 ● 不继承构造函数    继承属性(容纳型, 计算型), 方法, 索引subscript. 

    继承构造函数的特例: 没有指定构造函数 --> 继承父类所有构造函数

                     重写父类所有指定构造函数 --> 继承父类所有简易构造函数 

 详细搜本博文:  构造函数的继承

 ● 重载父类的方法或属性加上override

   适用于: 方法, 计算型属性, subscript

   区分自身与父类的方法与实例: self, super

protocol ClassNameAchievable {
    var className: String { get }
}

class NullClass: ClassNameAchievable, CustomStringConvertible {
    var className: String {
        return "NullClass"
    }
    var description: String {
        return self.className
    }
}

class FirstChildClass: NullClass {
    override var className: String { // 重载计算型属性className
        return "FirstChildClass"
    }
}

 

 动态结合与类型转换

 动态结合: 运行时候判别类型进行条件分歧 

 

 is  判定是否是该类或者继承该类
 as, as?, as!
 as  普通型转换
 as?

 可能会失败, 失败返回nil

 downcast可能会失败

 as!

 理论上可能会失败, 但是此处一定成功。失败则报错

 downcast可能会失败

 upcast: 朝上cast, cast成父类, 父父类等

 downcast: 朝下cast, cast成子类等, 可能会失败

 

 

 类方法与类属性

 

 静态属性与静态方法

 前面加static

 用例: 静态属性设定默认值, 默认模式, 静态方法改变默认模式等

 ● 计算型属性和容纳型属性都可以定义

 

 类属性与类方法

 前面加class

 区别:

 ● 属性: 只能定义计算型属性

 ● 子类可以改写类属性, 类方法

 

class NullClass {
    class var sampleStr: String { // 类属性
        return "NullClass"
    }
    
    class func getClassName() -> String { // 类方法
        return sampleStr
    }
}

class FirstChildClass: NullClass {
    override class var sampleStr: Str { // 改写类属性
        return "FirstChildClass"
    }

    override class func getClassName() -> String { // 改写类方法
        return "overrided " + sampleStr
    }
}

 

 

 

   

 

 继承与方法的呼出

 所有方法内部对其他类内部方法的呼出都相当于self.方法名(参数)

 类方法与实例方法都是

class A {
    func getName() -> String {
        return "A"
    }

    func printName() {
        print(getName()) // 内部的呼出相当于self.方法名
    }
}

class B: A {
    override func getName() -> String {
        return "B"
    }
}

A().printName() // A
B().printName() // B

 

   
   
构造函数
 指定初始函数和简易初始函数
init(参数) { //指定构造函数
    ... // 初始化该类新增的属性
    super.init(...)        // 没有父类时不需要这行
    ... //更改父类里的属性
}

convenience init(参数) {
// 简易构造函数 self.init(参数) // 先呼出带有指定构造函数的函数 ... // 变更所有需要变更的属性 }

 ● 指定构造函数(designated initializer): 仅靠来初始化的构造函数

             基类的默认构造函数是指定构造函数

      类不可用全项目构造函数(违反封装原则)

    简易构造函数(convenience initializer): 呼出本类的其他构造函数再进行附加处理的构造函数

 ● 子类必须含有至少一个指定构造函数(只呼出父类的某个指定构造函数, 不呼出本类的其他构造函数)

 ● 父类的指定构造函数改为简易构造函数的重载加override convenience, 关键词顺序不限

   父类的简易构造函数的重载不要override, 因为不会被子类呼出

class Sample {
    var a:Int
    init(a: Int) { // 指定构造函数
        self.a = a
    }
}


class Sample2: Sample {
    var b: Int
    init(a: Int, b: Int) { // 指定构造函数
        self.b = b
        super.init(a: a)
    }

    override convenience init(a:  Int) { // 改为简易构造函数重载, 关键词顺序不限
        self.init(a: a, b: 0)
    }
}

 

 ● 构造函数是否相同只看参数数量, 参数名(最外侧标签), 参数型

  无视是否可失败

init(a: Int) {
    ...
}

init(a aaaa: Int)? { // 两个被看成相同
    ...
}

 

 初始化的步骤和规则

 ● (a)指定构造函数必须先初始化新增的属性才能呼出super.init(...)

    (b)指定构造函数不能在super.init(...)之前设定super里有的属性

init(参数) { //指定构造函数
    ... // 初始化该类新增的属性
    super.init(...)
    ... //更改父类里的属性
}

 ● 简易构造函数必须先self.init(...), 也就是必须先呼出指定构造函数/呼出内部呼出指定构造函数的简易构造函数

  因为指定构造函数运行完以前的设定会被更改

    由此简易构造函数不能设定let量

convenience init(参数) { // 简易构造函数
    self.init(参数)  // 先呼出带有指定构造函数的函数
    ...  // 变更所有需要变更的属性
}

 

 ● 所有属性初始化完成以前不能使用实例变量, 实例方法, 不能把self作为值来用

 用的话写成更外层的函数(如全局函数)或写成静态方法/类方法

 ● 不可生成指定构造函数和简易构造函数的合体构造函数

    不可用条件分歧(if等)既有super.init(...) 呼出父类指定构造函数,

  也有self.init(...) 呼出自身的其他构造函数

 构造函数的继承

 满足一下任意条件则继承一部分

 ● 子类一个指定构造函数也没有

    继承所有指定构造函数 -> 继承所有简易构造函数

    -> 继承所有构造函数

 ● 子类带有父类所有指定函数

 (a) 和上面一样, 子类一个指定构造函数也没有。

   (b) 子类重载了所有指定构造函数

   继承所有简易构造函数

 

总结: 只新建简易构造函数则继承父类所有构造函数

           没有指定构造函数 --> 继承父类所有构造函数

重写父类所有指定构造函数 --> 继承父类所有简易构造函数 

 

 可能会失败的构造函数

 类, 构造体可以定义init?(...) { ... }

 ● 呼出父类的可失败构造函数的也变成可失败构造函数

   因为失败时 return nil , 整个构造函数都结束

 ● 构造函数是否相同只看参数数量, 参数名(最外侧标签), 参数型

  无视是否可失败

init(a: Int) {
    ...
}



init(a aaaa: Int)? { // 两个被看成相同
    ...
}

 

 必须构造函数

 required initializer 

 所有子类都必须带的构造函数, 名字前加上required

required init(...) {
    ...
}

 ● 重载不需要override, 因为所有子类都必须具有

 ● 子类的该构造函数也必须加上required

 ● 多个关键词是顺序不限

required convenience init(...) { ... }

convenience required init(...) { ... } // 两者一样, 关键词顺序不限

 

 排他性法则

 同一个变量同时只接受一个可读写的接入

 目的: 增加安全性

var sampleArray = [1, 2, 3]
swap(&sampleArray[0], &sampleArray[2]) // 报错

sampleArray.swapAt(0, 2) // 用swapAt(_:_:)

 

   
   
   
继承与子类的定义
 属性的继承

 

class Sample1 {
    var a: Int = 0
}

class Sample2: Sample1 {
    override var a: Int {
        get {
            return super.a // super获取上一级
        }
        set {
            super.a = newValue
        }
    }
}

 

 可以以计算型属性来重载父类的计算型, 容纳型属性

 ● 获取父类的属性 super.属性名

 ● 容纳型属性是重载必须同时有get, set

 ● 计算型属性重载

    只有get --->  { get }, { get set } 都可

   {get set} ---> 必须{ get set}

 

 索引的继承

 subscript 

 

 

class Sample1 {
    let array = [1, 2 ,3 ,4 ,5]
    subscript(id: Int) -> Int? {
        return id < array.count ? array[id] : nil
    }
}

class Sample2: Sample1 {
    override subscript(id: Int) ->Int { // 重载索引
        return id < self.array.count ? (super[id]! * super[id]!) : nil
    }
}

 ● 获取父类的索引: super[索引] 

 继承与属性监听 

 可以添加属性监听, 加override

 ● 可加在父类的容纳型属性与计算型属性里

    只可读属性不可加监听

 ● 同类内部同一个属性, { get set } { didSet willSet } 不可共存

 ● 重载以后原监听不会失效。按从进到远顺序运行

 不允许重载的设定 

 加上关键词finnal

 ● 可用于类, 类属型, 类方法

指定final的无法被继承, 指定final的属性/方法无法被重载
final class Sample { // 指定final的无法被继承
    // 指定final的属性/方法无法被重载
    final var property: Int = 0  // 指定final的属性
    final func sample() -> Bool { // 指定final的方法
        return false
    }
}

 ● 优点: 减少动态结合, 提高运行速度

    缺点: 不好维护(不好扩张和继承)

 类与协议 

 类来实现协议时

 

   类  构造体
 static 属性/方法

 可以以静态或类属性/方法来实现

 static/class

 以静态属性/方法来实现

 static

 mutating

 参照型,

 不需要mutating

 

 改变自身

 要加mutating

 构造函数

 成为必须构造函数

 required

  ● 带final的不需要required, 因为不会被继承

 构造函数来实现
     
     

 

 为类定义准备的协议 

 ● 只有类, 结构体, 枚举型可以采用协议

 ● 只运行类采用的协议定义方法

protocol 协议名: class, 继承的协议 { // 协议名后加上class
    ... // 方法不用也不能附mutating
}

 

    
类与型

 类型转换运算符

 cast operator

  

 is

 式 is T

 左边是否是是T型或者T的子类,

 左边是否继承了T协议

 as  式 as T

 转换为typealias声明的型, ObjC的兼容的型等

 必须一定会成功

 ● 无视元组标签

   带不带标签都不影响转换

 ● upcast用as, 即从子类转为父类, 父父类等...

 ● 不可用于downcast, 即转为子类

# TODO: Supply: [T是协议时的情况]

 as?  式 as? T

 ● downcast专用, 失败返回nil

   即转为子类

 ● upcast一定成功, 用as?/as!会有警告

# TODO: Supply: [T是协议时的情况]

 as!  式 as! T  ● downcast专用, 失败报错
     

 

 分歧语法与类型转换   
case let/var 变量名 as

 ● 例

switch sample { // 子类放在上方
    case let s = sample as FirstChildClass: // 是FirstChildClass或子类
        ...
    case let s = sample as NullClass: // 是NullClass或其子类
        ...
    default:
        ...
}

 ● if-let和if-case的例

if let v = obj as? Sample { // if-let, if-var
    ...
}


if case let v as Sample = obj { // if-case
    ...
}

 ● case条件写的时候和switch里一样, 加上 = 测试对象 

case (1, 1..<100) = (x, y)

 

 meta type和dynamic type

 

 dynamic type

 也叫type instance, 型自身的实例

 程序里所有的 类型. 等都相当于 类型.self. 等

 占内存, 是实体

 

 获取

 类型.self

 ● 实例.self 返回实例自己, 不是类型

"hello".self // "hello"
1.self // 1

 type(of: 型实例)

type(of: Int(1)) // Int.self
print(type(of: Int(1))) // Int

 ● 可用于所有数据类型 (类与结构体)

    (除类与闭包外所有数据都是结构体)

   
   

  ● 判断类型时使用的是类型实例

type(of: Int(1)) == Int.self // true

 

 

 meta type

 类型本身, 不占内存

类型.Type

 

 ●  协议.Protocol 获取协议的meta type(原型), 表示该协议自身meta type

 ● 协议作型时,  协议.Type 表示采用该协议的型

    (协议内部不能有附属型 associatedtype , 不能有 Self )

   具体型为采用该协议的型

class Sample: CustomStringConvertible { ... }
var type: CustomStringConvertible.Type = Sample.self // 协议型

 

 继承与Self 

 ● Self只可用于class内部函数的返回型和protocol

 ● Self表示当前的类

 

 协议与类的合成

  typealias Sample = ClassAndTypeTest & CustomStringConvertible & Equatable 

 表示采用协议的类或其子类

 类似于协议的合成

 继承与类型推导 

 ● 包含不同型元素的数组

    AnyObject: 任何类的型

    Any: 任何类的型与任何构造体

var array1: [AnyObject] = [ ClassA(), ClassB(), ClassC() ]

var array2: [Any] = [ ClassA(), 1, "Hello" ]

 ● 获取时要自己类型转换, as/as?/as!

 ● AnyObject主要用于和Objc交换信息

 继承Obejctive-C类 

和正常继承一样

 ● 除了可用继承类的功能, 还可用作为Obejctive-C类的功能

    只是想要作为ObjC类来用的话继承NSObject

   
   
   
释放时的处理
 释放时的处理与析构函数

 ● 必要性

   读写文件的变量释放前关闭文件等, 动态释放内存

 析构函数的定义 
deinit {
    ...
}

 ● 释放前自动呼出, 只呼出一次

 ● 从最下层子类一层层往上呼出

   没定义的地方跳过, 继续网上

 ● 内部处理不限, 但不能妨碍释放(如把要释放的实例代入到全局变量等)

 

 析构函数的例   
class DeinitSampleClass {
    deinit {
        print("deinited")
    }
}


var deinitTest: DeinitSampleClass? = DeinitSampleClass()
deinitTest = nil
deinitTest = DeinitSampleClass()
deinitTest = nil
deinitTest = DeinitSampleClass()


/*输出结果
----------------
deinited
deinited
deinited
/*

 

   
posted @ 2018-03-05 17:03  懒虫哥哥  阅读(325)  评论(0编辑  收藏  举报