Swift 笔记 - 面向对象

类的定义

所有的属性和方法都是全局共享的,不需要 import

1
2
3
4
5
6
7
8
import UIKit
// 定义 Person 类,继承自 NSObject
class Person: NSObject {

// 对象属性应该可变的,可选项允许变量为空
var name: String

}

构造函数

给属性分配空间,设置初始值,父类提供了构造函数需要对父类的构造函数进行扩展,使用关键字override

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import UIKit
class Person: NSObject {

var name: String

override init() {
// name初始化只能在 super.init() 上面
name = "Zhang San"
super.init()
// name = "Zhang San" 再此处写会报错
}
}
// 实例化
let p = Person()
print(p.name); // Zhang San

函数重载

函数名相同,参数个数和类型不同

1
2
3
4
5
6
7
8
9
class Person: NSObject {
var name: String
var age: Int
// 如果重载构造函数,系统默认构造函数就不能再被访问
init(name: String, age: Int) {
self.name = name
self.age = age
}
}

KVC

Key-value coding,通过Key名直接访问对象属性或赋值,而不需要调用明确的存取方法

1
2
3
4
5
6
7
8
9
10
11
12
class Person: NSObject {
var name: String?
// 设置 age 的初始值,因为Int是基本类型,不存在 nil 的概念,所以不能写成 var age: Int?
var age: Int = 0

init(dict: [String: Any]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
}
// 实例化并传入参数 dict
let p = Person(["name": "Zhang San", "age": 20])

便利构造函数

能够提供条件检测,允许返回nil,默认的构造函数必须要创建对象,便利构造函数必须以self.的方式调用其他构造函数创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person: NSObject {
var name: String?
var age: Int = 0

init(dict: [String: Any]) {
super.init()
setValuesForKeysWithDictionary(dict)
}

convenience init?(name: String, age: Int) {
if age < 0 || age > 100 {
return nil
}
// 调用其他构造函数初始化属性
self.init(dict: ["name": name, "age": age])
}
}
let p = Person("name": "Zhang San", age: 101)
// 一般解包时需要用 ! ,但此时会报错
print("name: \(p!.name), age: \(p!.age)")
// 需要用 ? 来解包,表示如果p为nil,就不会继续调用后续的属性或方法
print("name: \(p?.name), age: \(p?.age)"); // name: nil, age: nil

对象释放方法,与dealloc类似,主要负责对象被销毁前的内存释放工作,不允许重载,不允许带参数,不允许直接调用,会先调用子类的释放函数再调用父类的

需要程序员销毁的内容

  • 通知 (不注销不会崩溃)
  • KVO (不注销会崩溃)
  • NSTimer (会对target (self) 进行强引用)
1
2
3
4
5
class Person: NSObject {
deinit {
print("Person deinit")
}
}

懒加载

本质上是一个闭包,第一次访问属性时会执行后面的闭包,将返回值保存在属性中,下次访问就不会执行闭包,直接获取属性的值

如果没有lazy,会在initWithCoder方法中被调用,当二进制的storyboard被还原成视图控制器对象后,就会被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import UIKit
class ViewController: UIViewController {
// 懒加载 Person 类
lazy var person: Person = {
print("懒加载")
return Person()
}()

// 还可以写成以下格式
let personFunc = { () -> Person in
print("懒加载")
return Person()
}
lazy var demoPerson: Person = self.personFunc()

// 懒加载的简单写法
lazy var demoPerson: Person = Person()

// 闭包中的 self. 是不能省略的
var name: String? = "Zhang San"
lazy var title: String? = {
return "Mr " + (self.name ?? "")
}
// 输出 Mr Zhang San
}

getter和setter

在Swift中极少用,仅供参考
OC中使用getter进行懒加载,而Swift提供了lazy关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person: NSObject {
private var _name: String
var name: String? {
get {
// 返回 _成员变量的值
return _name
}
get {
// 使用 _成员变量记录新的值
_name = newValue
}
}
}
// 使用
let p = Person()
p.name = "Zhang San"
print(p.name); // Zhang San

read only

只读属性,也叫计算型属性,每次调用时都会执行大括号中的代码

只写getter方法

1
2
3
4
5
6
7
8
9
10
print var _name: String?
var name: String? {
get {
return _name
}
}
// 只读属性可以简写,省略 get{}
var name: String? {
return _name
}

懒加载与计算型属性的区别

懒加载

  • 只计算一次
  • 需要开辟单独的空间保存计算结果
  • 闭包的代码再也不会被调用
  • 如果计算量很大,需要提前准备

计算型属性

  • 每次都要计算,浪费性能
  • 不需要开辟额外的空间

类函数(静态函数)

1
2
3
4
5
6
7
8
9
10
import UIKit
class Person: NSObject {
// 定义静态成员属性
static var text: String = "Test"
// 定义类函数(静态函数)
class func staticFunc() {
print("类函数")
}
}
// 调用

单例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import UIKit
class Person: NSObject {
// 定义静态成员属性
static var instance: Person?
// 定义标识,只让diepatch_once中的代码运行一次
static var onceToken: dispatch_once_t = 0
// 提供全局访问类
class func person() -> Person {
dispatch_once(&onceToken) { () -> Void in
// 实例化类
instance = Person()
}
// 强制解包并返回
return instance!
}
// 单例的懒加载写法
static let person2: Person = {
return Person()
}()
}

extension

对在已存在的类、类型进行扩展,但不能覆盖现有功能

计算属性

1
2
3
4
5
6
7
8
9
10
11
extension Double {
var km: Double { return self * 1000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1000.0 }
// 英尺
var ft: Double { return self / 3.28084 }
}
// 使用
let threeFeet = 3.ft
print("3英尺是 \(threeFeet) 米"); // 3英尺是 0.914399970739201 米

扩展方法

以下例子是对Int使用,也可以对自定义的类使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
// 使用(尾随闭包的形式)
3.repetitions {
print("Hello"); // Hello Hello Hello
}
// 使用(普通传参)
3.repetitions(task: {
print("Hello"); // Hello Hello Hello
})

视图模型

模型通常继承自NSObject,可以使用KVC设置属性,简化对象构造
而视图模型没有父类,所有内容都要从头构造

视图模型是封装业务逻辑,通常没有复杂的属性

1
2
3
4
5
6
7
8
9
import Foundation
class UserAccountViewModel {
// 用户模型
var account: UserAccount?
// 构造函数
init() {

}
}

协议

类似接口,用于制定属性和方法,让其他遵守协议的地方实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import UIKit
protocol Pet {
// 协议属性,如果只有 get 则是只读属性
var name: String { get set }
// 协议方法
func playWith()
func fed()
}
class Animal {
var type: String = "mammal"
}
// 继承父类同时实现协议,需要先写父类,再写协议
class Dog: Animal, Pet {
var name: String = "Puppy"
func playWith() {
print("wong")
}
func fed() {
print("I love Bones")
}
}

属性观察者

在属性即将赋值、赋值后进行操作,默认赋值和init初始化方法中的赋值不会触发属性观察者

1
2
3
4
5
6
7
8
9
10
11
12
13
import UIKit
class person {
var age = 0 {
// 即将赋值时
willSet(newAge) {
print("原值:\(age),即将被赋值为:\(newAge)")
}
// 赋值后
didSet(oldAge) {
print("原值:\(oldAge),即将被赋值为:\(age)")
}
}
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!