创建: 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: )
}
}

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

无视是否可失败

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 = [, , ]
swap(&sampleArray[], &sampleArray[]) // 报错 sampleArray.swapAt(, ) // 用swapAt(_:_:)
   
   
   
继承与子类的定义
 属性的继承
class Sample1 {
var a: Int =
} 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 = [, , , ,]
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 = // 指定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 (, ..<) = (x, y)
 meta type和dynamic type
 dynamic type

也叫type instance, 型自身的实例

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

占内存, 是实体

 获取

类型.self

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

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

type(of: 型实例)

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

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

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

   
   

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

type(of: Int()) == 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(), , "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
/*
   

Swift4 类与继承, 类型转换, 类型判断的更多相关文章

  1. js对象类型判断工具

    对象类型判断工具 /** *类功能:对象类型判断工具 **/ var TypeUtil = { /** *方法说明:是否是数组 **/ isArray: function (obj) {//是否是数组 ...

  2. C#与Java对比学习:类型判断、类与接口继承、代码规范与编码习惯、常量定义

    类型判断符号: C#:object a;  if(a is int) { }  用 is 符号判断 Java:object a; if(a instanceof Integer) { } 用 inst ...

  3. java关键字extends(继承)、Supe(父类引用空间)、 This(方法调用者对象)、Instanceof(实例类型-判断对象是否属于某个类)、final(最终)、abstract(抽象) 、interface(接口)0

    java 继承使用关键字extends   继承的作用:减少代码量,优化代码 继承的使用注意点: 1子类不能继承父类的私有变量 2.子类不能继承父类的构造方法 3.子类在调用自己的构造方法时 会默认调 ...

  4. java面向对象类的继承~ 匿名类 ;多态特性;强制类型转换

    类的继承 创建子类语法:     修饰符 class 子类名 extends 父类名{        } 匿名子类语法: 直接实例化,过程中通过匿名类 继承父类,在实例化过程中将子类匿名 <父类 ...

  5. c#中判断类是否继承于泛型基类

    在c#中,有时候我们会编写类似这样的代码: public class a<T> { //具体类的实现 } public class b : a<string>{} 如果b继承a ...

  6. Java 中 父类变量访问子类方法 需要使用 类型转换 (instenceof)关键字 /类型判断/

    通过数组元素访问方法的时候只能访问在 Animal中定义的方法,对 于 Tiger类和  Fish中定义的方法时却不能调用,例如语句  animal[2].swim();就是不正确的.当 需要访问这些 ...

  7. 类型和原生函数及类型转换(二:终结js类型判断)

    typeof instanceof isArray() Object.prototype.toString.call() DOM对象与DOM集合对象的类型判断 一.typeof typeof是一个一元 ...

  8. Go 类型转换与类型判断

    目录 Go 类型转换与类型判断 1.类型转化 2.类型判断 Go 类型转换与类型判断 1.类型转化 T(a) : T 是目标类型 a 是源变量 package main import "fm ...

  9. asp.net 的page 基类页面 做一些判断 可以定义一个基类页面 继承Page类 然后重写OnPreLoad事件

    public class BasePage:Page protected override void OnPreLoad(EventArgs e){     base.OnPreLoad(e);    ...

随机推荐

  1. 用 Gearman 分发 PHP 应用程序的工作负载【转载】

    通过本文,了解工作分发系统 Gearman 并分发用 PHP.C.Ruby 及其他受支持语言编写的应用程序的工作负载. 尽管一个 Web 应用程序的大部分内容都与表示有关,但它的价值与竞争优势却可能体 ...

  2. tomcat启动提示java.lang.UnsatisfiedLinkError: D:\soft\devTool\apache-tomcat-7.0.57\bin\tcnative-1.dll: C

    https://blog.csdn.net/a274360781/article/details/52411984

  3. softmax函数理解

    https://www.zhihu.com/question/23765351   因为这里不太方便编辑公式,所以很多公式推导的细节都已经略去了,如果对相关数学表述感兴趣的话,请戳这里的链接Softm ...

  4. ZOJ 1232 【灵活运用FLOYD】 【图DP】

    题意: copy自http://blog.csdn.net/monkey_little/article/details/6637805 有A个村子和B个城堡,村子标号是1~A,城堡标号是A+1~B.马 ...

  5. UVA 4857 Halloween Costumes 区间背包

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  6. vue-cli中process.env配置以及打包本地运行或者线上运行配置

    我们知道打包默认npm run build,可是打包后点击dist文件中index.html一片空白.问题在于路径问题.我们在工程文件的最外层增加文件.env.production这个文件就是这么奇怪 ...

  7. 【Mongodb教程 第八课 】MongoDB 更新文档

    MongoDB的 update() 和 save() 方法用于更新文档的集合. update()方法更新现有的文档值,而替换现有的文档通过的文件中 save() 方法. MongoDB Update( ...

  8. MyEclipse搭建SSH(Struts2+Spring2+Hibernate3)框架项目教程

    对Struts.spring.hibernate大体上了解一遍后,就是针对这个几个框架的整合了. 怎样整合,请看以下: 第一:Struts2的jar和xml配置文件: jar包: commons-fi ...

  9. c++中拷贝构造函数,浅拷贝和深拷贝的区别

    在C++提供了一种特殊的构造函数,称为拷贝构造函数.拷贝构造函数具有一般构造函数的所有特性,其作用是使用一个已经存在的对象(由拷贝构造函数的参数指定的对象)去初始化一个新的同类对象,即完成本类对象的复 ...

  10. Makefile详解 (转--不错就是有点长)

    概述 —— 什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和 professional的程序员,make ...