方法
Swift
中枚举
、结构体
、类
中都可以定义方法,方法分为实例方法
和类型方法
实例方法
(Instance Method):通过实例调用类型方法
(Type Method):通过类型调用,用static
或者class
关键字定义
class Car {
static var count = 0
init() {
Car.count += 1
}
// 类型方法
static func getCount() -> Int {
return count
}
// 实例方法
func getNum() -> Int {
return 2
}
}
let c0 = Car()
let c1 = Car()
let c2 = Car()
// 类型方法调用
print(Car.getCount()) // 3
// 实例方法调用
let c3 = Car();
c3.getNum();
mutating
结构体
和枚举
是值类型,默认情况下,值类型的属性不能被自身的实例方法修改, 在func
关键字前加mutating
可以允许这种修改行为
@discardableResult
在func
前面加个@discardableResult
, 可以消除函数调用后返回值未被使用的警告
下标(subscript)
使用subscript
可以给任意类型(枚举、结构体、类)增加下标功能,有些地方也翻译为:下标脚本
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
subscript
中定义的返回值决定了get
方法的返回值类型,也决定了set
方法中newValue
的类型
subscript
可以接收多个参数,并且类型任意
subscript
可以没有set
方法,但必须要求get
方法,如果只有get
方法,可以省略get
class Point {
var x = 0.0, y = 0.0
subscript(index: Int) -> Double {
get { // 只有get方法,没有set方法, 可以编译通过
if (index == 0) {
return x
} else if (index == 1) {
return y
}
return 0
}
}
}
class Point1 {
var x = 0.0, y = 0.0
subscript(index: Int) -> Double {
// 如果只有get方法,可以省略get
if (index == 0) {
return x
} else if (index == 1) {
return y
}
return 0
}
}
下标的细节
下标
可以设置参数标签,如果设置参数标签,则使用时必须加上标签访问
class Point1 {
var x = 0.0, y = 0.0
subscript(index i: Int) -> Double {
// 如果只有get方法,可以省略get
if (i == 0) {
return x
} else if (i == 1) {
return y
}
return 0
}
}
var p = Point1()
p.y = 22.2
print(p[index: 1]) // 22.2
下标也可以是类型方法
class Sum {
static subscript(v1: Int, v2: Int) -> Int {
return v1 + v2
}
}
print(Sum(10, 20))
结构体、类作为返回值对比
接收多个参数的下标
继承(Inheritance)
值类型(枚举、结构体)不支持继承,只有类支持继承
没有父类的类,称为基类
, Swift中并没有像OC、Java那样的规定:任何类最终都要继承自某个基类
子类可以重写父类的下标
、方法
、属性
,重写必须加上override
关键字
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()
// Animal speak
// Cat speak
anim.speak()
print(anim[6]) // 7
重写类型方法、下标
- 被
class
修饰的类型方法
、下标
,允许
被子类重写 - 被
static
修饰的类型方法
、下标
,不允许
被子类重写
class Animal {
class func speak() {
print("Animal speak")
}
class subscript(index: Int) -> Int {
return index
}
}
class Cat: Animal {
override static func speak() {
super.speak()
print("Cat speak")
}
override static subscript(index: Int) -> Int {
return super[index] + 1
}
}
class WhiteCat: Cat {
override static func speak() { // 编译报错:Cannot override static method
super.speak()
print("WhiteCat speak")
}
}
重写属性
子类
可以将父类
的属性(存储
、计算
)重写为计算属性
子类
不可以将父类
重写为存储属性
- 只能重写
var
属性,不能重写let
属性 - 重写时,
属性名
、类型
要一致 - 子类重写后的属性权限
不能小于
父类属性的权限- 如果父类属性是
只读
的,那么子类重写后的属性可以是只读的
,也可以是可读写的
- 如果父类属性是
可读写
的,那么子类重写后的属性也必须
是可读写
的
- 如果父类属性是
重写实例属性
class Circle {
var radius: Int = 0
var diameter: Int {
set {
print("Circle setDiameter")
radius = newValue / 2
}
get {
print("Circle getDiameter")
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)
// 子类SubCircle
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
// 12
print(circle.diameter)
// SubCircle setDiameter
// Circle setDiameter
// SubCircle setRadius
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
}
}
}
// 子类SubCircle
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(Circle.diameter)
// SubCircle setDiameter
// Circle setDiameter
SubCircle.diameter = 20
// 10
print(SubCircle.radius)
属性观察器
可以在子类
中为父类属性
(除了只读计算属性
、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
final
被final
修饰的方法
、下标
、属性
,禁止被重写
被final
修饰的类
,禁止被继承
初始化器
类、结构体、枚举
都可以定义初始化器;
类
有两种初始化器: 指定初始化器
(designated initializer)和便捷初始化器
(convenience initializer),
// 指定初始化器
init(parameters) {
statements
}
// 便捷初始化器
convenience init(parameters) {
statements
}
每个类
至少有一个指定初始化器
(类默认就有),指定初始化器是类的主要初始化器,但假如自己去实现一个指定初始化器,类的默认初始化器就不再自动生成,调用类时,必须使用自己实现的指定初始化器,类偏向于少量的指定初始化器,一个类通常只有一个指定初始化器
初始化器的相互调用规则
- 指定初始化器必须从它的直系父类调用指定初始化器
- 便捷初始化器必须从相同的类里调用另外一个初始化器
- 便捷初始化器最终必须调用一个指定初始化器
Swift中多态的实现原理
class Animal {
func speak() {
print("Animal speak")
}
func eat() -> Void {
print("Animal eat")
}
func sleep() {
print("Animal sleep")
}
}
class Dog: Animal {
override func speak() {
print("Dog speak")
}
override func eat() {
print("Dog eat")
}
func run() {
print("Dog run")
}
}
var anim : Animal
anim = Animal()
anim.speak(); // Animal speak
anim.eat(); // Animal eat
anim.sleep(); // Animal sleep
anim = Dog(); // anim指向了Dog类,所以在找anim的sleep等方法时会向Dog类中找
anim.speak(); // Dog speak
anim.eat(); // Dog eat
anim.sleep(); // Animal sleep
//dog.run() // Dog run
本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.