- 作者: Liwx
- 郵箱: 1032282633@qq.com
- 源碼: 需要
源碼
的同學(xué), 可以在評(píng)論區(qū)
留下您的郵箱
iOS Swift 語(yǔ)法
底層原理
與內(nèi)存管理
分析 專題:【iOS Swift5語(yǔ)法】00 - 匯編
01 - 基礎(chǔ)語(yǔ)法
02 - 流程控制
03 - 函數(shù)
04 - 枚舉
05 - 可選項(xiàng)
06 - 結(jié)構(gòu)體和類
07 - 閉包
08 - 屬性
09 - 方法
10 - 下標(biāo)
11 - 繼承
12 - 初始化器init
13 - 可選項(xiàng)
目錄
- 01-屬性
- 02-存儲(chǔ)屬性
- 03-計(jì)算屬性
- 04-枚舉rawValue原理
- 05-延遲存儲(chǔ)屬性(Lazy Stored Property)
- 06-延遲存儲(chǔ)屬性注意點(diǎn)
- 07-屬性觀察器(Property Observer)
- 08-全局變量、局部變量
- 09-inout的再次研究
- 10-inout的本質(zhì)總結(jié)
- 11-類型屬性(Type Property)
- 12-類型屬性細(xì)節(jié)
- 13-單例模式
01-屬性
- Swift中跟實(shí)例相關(guān)的屬性可以分為
2大類型
:存儲(chǔ)屬性
(Stored Property),計(jì)算屬性
(Computed Property)
- 存儲(chǔ)屬性
- 類似于
成員變量
這個(gè)概念 存儲(chǔ)在實(shí)例的內(nèi)存中
-
結(jié)構(gòu)體、類
可以定義存儲(chǔ)屬性 -
枚舉
不可以定義存儲(chǔ)屬性
- 類似于
- 計(jì)算屬性
-
本質(zhì)
就是方法
(函數(shù)) 不占用實(shí)例的內(nèi)存
-
枚舉、結(jié)構(gòu)體、類
都可以定義計(jì)算屬性
-
struct Circle {
// 存儲(chǔ)屬性 占用內(nèi)存
var radius: Double
// 計(jì)算屬性 不占用內(nèi)存
var diameter: Double {
set {
radius = newValue / 2
}
get {
radius * 2 // 如果只有單一表達(dá)式,可以省略return
}
/*
也可以這樣寫
set(newDiameter) {
radius = newDiameter / 2
}
*/
}
}
var circle = Circle(radius: 5)
print(circle.radius) // 5.0
print(circle.diameter) // 10.0
circle.diameter = 12
print(circle.radius) // 6.0
print(circle.diameter) // 12.0
print(MemoryLayout<Circle>.stride) // 8
02-存儲(chǔ)屬性
- 關(guān)于存儲(chǔ)屬性, Swift有個(gè)明確的規(guī)定
- 在創(chuàng)建
類
或者結(jié)構(gòu)體
的實(shí)例時(shí),必須為所有的存儲(chǔ)屬性
設(shè)置一個(gè)合適的初始值
- 可以在
初始化器里
為存儲(chǔ)屬性設(shè)置初始值
- 可以分配一個(gè)
默認(rèn)的屬性
值作為屬性定義的一部分
- 可以在
- 在初始化器里為存儲(chǔ)屬性設(shè)置一個(gè)初始值
struct Point {
var x: Int
var y: Int
init() {
x = 11
y = 22
}
}
- 分配一個(gè)默認(rèn)的屬性值作為屬性定義的一部分
struct Point {
var x: Int = 11
var y: Int = 22
}
var p = Point()
03-計(jì)算屬性
-
set
傳入的新增默認(rèn)叫做newValue
,也可以自定義
struct Circle {
var radius: Double
var diameter: Double {
// set(newDiameter) { // 自定義
// radius = newDiameter / 2
// }
set {
radius = newValue / 2
}
get {
radius * 2
}
}
}
- 定義
計(jì)算屬性
只能用var
, 不能用let
-
let
代表常量:值是一成不變
的 - 計(jì)算屬性的值是可能
發(fā)生變化
的(即使是只讀計(jì)算屬性
)
-
-
只讀計(jì)算屬性
: 只有get
, 沒有set
struct Circle {
var radius: Double
var diameter: Double {
get {
radius * 2
}
}
}
- 只讀計(jì)算屬性, 只有
get
方法,可以省略get
struct Circle {
var radius: Double
var diameter: Double { radius * 2 }
}
- 注意:
不能只有set方法, 沒有g(shù)et方法
04-枚舉rawValue原理
-
枚舉原始值rawValue
的本質(zhì)是:只讀計(jì)算屬性
enum TestEnum : Int {
case test1 = 1, test2 = 2, test3 = 3
var rawValue: Int {
switch self {
case .test1:
return 10
case .test2:
return 11
case .test3:
return 12
}
}
}
print(TestEnum.test3.rawValue) // 12
05-延遲存儲(chǔ)屬性(Lazy Stored Property)
- 使用
lazy
可以定義一個(gè)延遲存儲(chǔ)屬性
, 在第一次用到屬性
的時(shí)候才會(huì)進(jìn)行初始化
-
lazy
屬性必須是var
, 不能是let-
let
必須在實(shí)例的初始化方法完成之前
就擁有值
-
- 如果
多條線程同時(shí)第一次訪問lazy屬性
- 無法保證屬性只被初始化1次 (
不是線程安全
)
- 無法保證屬性只被初始化1次 (
class Car {
init() {
print("Car init!")
}
func run() {
print("Car is running!")
}
}
class Person {
lazy var car = Car()
// lazy let car1 = Car() // 延遲存儲(chǔ)屬性必須用var, error: 'lazy' cannot be used on a let
init() {
print("Person init!")
}
func goOut() {
car.run()
}
}
let p = Person() // 還沒用到car延遲屬性,因此此次不會(huì)創(chuàng)建car屬性
//Person init!
print("------")
p.goOut() // 用到car延遲屬性,此時(shí)才創(chuàng)建Car實(shí)例對(duì)象,調(diào)用Car init 方法
//Car init!
//Car is running
- 應(yīng)用場(chǎng)景
class PhotoView {
lazy var image: Image = {
let url = "https://upload-images.jianshu.io/upload_images/1253159-f30fb4cd197ff37d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"
let data = Data(url: url)
return Image(data: data)
}()
}
06-延遲存儲(chǔ)屬性注意點(diǎn)
- 當(dāng)
結(jié)構(gòu)體包含一個(gè)延遲存儲(chǔ)屬性
時(shí), 只有var
才能訪問延遲存儲(chǔ)屬性- 因?yàn)?code>延遲存儲(chǔ)屬性初始化時(shí)
需要改變結(jié)構(gòu)體的內(nèi)存
- 因?yàn)?code>延遲存儲(chǔ)屬性初始化時(shí)
struct Point {
var x = 0
var y = 0
lazy var z = 0
}
/*
let p = Point() // 此處用let,不能訪問延遲存儲(chǔ)屬性p.z
print(p.z) // error: cannot use mutating getter on immutable value: 'p' is a 'let' constant
*/
var p = Point() // 此處需用var, 才能正常訪問p.z
print(p.z)
07-屬性觀察器(Property Observer)
-
willSet
會(huì)傳遞新值,默認(rèn)叫newValue
-
didSet
會(huì)傳遞舊值, 默認(rèn)叫oldValue
- 在
初始化器中設(shè)置屬性值
不會(huì)觸發(fā)willSet
和didSet
- 在
屬性定義時(shí)設(shè)置初始值
也不會(huì)觸發(fā)willSet
和didSet
- 可以為非
lazy
的var 存儲(chǔ)屬性
設(shè)置屬性觀察器
struct Circle {
var radius: Double {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, radius)
}
}
init() {
self.radius = 1.0
print("Circle init!")
}
}
// Circle init!
var circle = Circle()
// willSet 10.5
// didSet 1.0 10.5
circle.radius = 10.5
// 10.5
print(circle.radius)
08-全局變量、局部變量
屬性觀察器、計(jì)算屬性
的功能,同樣可以應(yīng)用在全局變量、局部變量
身上全局變量 計(jì)算屬性
// 全局變量 計(jì)算屬性 屬性觀察器應(yīng)用
var num: Int {
get {
return 10
}
set {
print("setNum", newValue)
}
}
num = 11 // setNum 11
print(num) // 10
- 局部變量 存儲(chǔ)屬性 屬性觀察器應(yīng)用
// 局部變量 存儲(chǔ)屬性 屬性觀察器應(yīng)用
func test() {
var age = 10 {
willSet {
print("willSet", newValue)
}
didSet {
print("didSet", oldValue, age)
}
}
age = 11
// willSet 11
// didSet 10 11
}
test()
09-inout的再次研究
struct Shape {
var width: Int
var side: Int {
willSet {
print("willSetSide", newValue)
}
didSet {
print("didSetSide", oldValue, side)
}
}
var girth: Int {
set {
width = newValue / side
print("setGirth", newValue)
}
get {
print("getGirth")
return width * side
}
}
func show() {
print("width=\(width), side=\(side), girth=\(girth)")
}
}
func test(_ num: inout Int) {
num = 20
}
var s = Shape(width: 10, side: 4)
test(&s.width)
s.show()
print("---------")
test(&s.side)
s.show()
print("---------")
test(&s.girth)
s.show()
// 運(yùn)行結(jié)果:
getGirth
width=20, side=4, girth=80
---------
willSetSide 20
didSetSide 4 20
getGirth
width=20, side=20, girth=400
---------
getGirth
setGirth 20
getGirth
width=1, side=20, girth=20
image.png
10-inout的本質(zhì)總結(jié)
- 如果
實(shí)參有物理內(nèi)存地址
,且沒有設(shè)置屬性觀察器
- 直接將
實(shí)參的內(nèi)存地址傳入函數(shù)
(實(shí)參進(jìn)行引用傳遞
)
- 直接將
- 如果實(shí)參是
計(jì)算屬性
或者 設(shè)置了屬性觀察器
- 采取了
Copy In Copy Out
的做法- 調(diào)用該函數(shù)是,先復(fù)制實(shí)參的值,產(chǎn)生
副本
[get]
- 將
副本的內(nèi)存地址
傳入函數(shù)(副本進(jìn)行引用傳遞
),在函數(shù)內(nèi)部可以修改副本的值 - 函數(shù)返回后,再將副本的值覆蓋實(shí)參的值
[set]
- 調(diào)用該函數(shù)是,先復(fù)制實(shí)參的值,產(chǎn)生
- 采取了
- 總結(jié): inout的本質(zhì)就是引用傳遞(地址傳遞)
11-類型屬性(Type Property)
- 嚴(yán)格來說, 屬性可以分為
-
實(shí)例屬性
(Instance Property): 只能通過實(shí)例去訪問-
存儲(chǔ)實(shí)例屬性
(Stored Instance Property): 存儲(chǔ)在實(shí)例的內(nèi)存中,每個(gè)實(shí)例都有1份
-
計(jì)算實(shí)例屬性
(Computed Instance Property)
-
- 類型屬性(Type Property): 只能通過類型去訪問
-
存儲(chǔ)類型屬性
(Stored Type Property): 整個(gè)程序運(yùn)行過程中,就只有1份內(nèi)存(類似于全局變量)
-
計(jì)算類型屬性
(Computed Type Property)
-
- 可以通過
static
定義類型屬性
- 如果是
類
,也可以
用關(guān)鍵字class
- 如果是
-
struct Car {
static var count: Int = 0
init() {
Car.count += 1
// 構(gòu)造器,類似實(shí)例方法,實(shí)例方法不能調(diào)用使用 count 或者self.count 調(diào)用類型屬性
// count += 1 // Static member 'count' cannot be used on instance of type 'Car'
}
}
let c1 = Car()
let c2 = Car()
let c3 = Car()
print(Car.count) // 3
12-類型屬性細(xì)節(jié)
-
存儲(chǔ)類型屬性
默認(rèn)就是lazy
, 會(huì)在第一次使用
的時(shí)候才初始化
- 就算被多個(gè)線程同時(shí)訪問,保證
只會(huì)初始化一次
(線程安全
) -
存儲(chǔ)類型屬性
可以是let
- 就算被多個(gè)線程同時(shí)訪問,保證
-
枚舉類型
也可以定義類型屬性
(存儲(chǔ)類型屬性、計(jì)算類型屬性
)
enum TestEnum : Int {
static var count: Int = 0 // 存儲(chǔ)類型屬性、計(jì)算類
case test1 = 1, test2 = 2
func plus() {
TestEnum.count += 1
}
static func getCount() -> Int { // 計(jì)算類型屬性
return count
}
}
var t1 = TestEnum.test1
t1.plus()
print(TestEnum.count)
t1.plus()
print(TestEnum.count)
t1.plus()
print(TestEnum.count)
print(TestEnum.getCount())
13-單例模式
public class FileManager {
public static let shared = FileManager() // 默認(rèn)是lazy 就算被多個(gè)線程同時(shí)訪問,保證`只會(huì)初始化一次`(線程安全)
private init() { }
}
public class FileManager {
public static let shared = { // 默認(rèn)是lazy 就算被多個(gè)線程同時(shí)訪問,保證`只會(huì)初始化一次`(線程安全)
// do something
return FileManager()
}()
private init() { }
}
let manager = FileManager.shared
iOS Swift 語(yǔ)法
底層原理
與內(nèi)存管理
分析 專題:【iOS Swift5語(yǔ)法】