Swift5.x入門09--結構體與類

結構體

  • 在Swift標準庫中,絕大多數公開的類型都是結構體,而枚舉和類只占很少的一部分;
  • Bool,Int,Double,String,Array,Dictionary等常見類型都是結構體;
struct Date {
    var year: Int
    var month: Int
    var day: Int
}

import Foundation

var date = Date(year: 2021, month: 7, day: 29)
  • 定義了一個結構體Date,其內部有三個成員;
  • 所有的結構體都有一個編譯器自動生成的初始化器(初始化方法,構造方法),例如Date(year: 2021, month: 7, day: 29)
  • 結構內部的所有成員,專業(yè)術語叫做存儲屬性

結構體的初始化器

  • 編譯器會根據情況,可能會為結構體生成多個初始化器,宗旨是:保證所有成員都有初始值;
struct Point {
    var x: Int = 0
    var y: Int = 0
}

import Foundation

var point = Point(x: 10, y: 10)
point = Point(x: 20)
point = Point(y: 30)
point = Point()
  • 由于成員x,y有默認值,所有編譯器會自動生成4個初始化器;
自定義初始化器
  • 在定義結構體時,一旦自定義了初始化器,編譯器就不會自動生成其他的初始化器;
struct Point {
    var x: Int = 0
    var y: Int = 0
    init(x: Int,y: Int) {
        self.x = x
        self.y = y
    }
}
  • init為自定義初始化器;
結構體的內存結構
struct Point {
    var x: Int = 0
    var y: Int = 0
    var origin: Bool = false
    init(x: Int,y: Int,origin: Bool) {
        self.x = x
        self.y = y
        self.origin = origin
    }
}

import Foundation

var point = Point(x: 10, y: 20,origin: true)

print(MemoryLayout<Point>.size) //17
print(MemoryLayout<Point>.stride) //24
print(MemoryLayout<Point>.alignment) //8

  • 結構體變量point的內存布局如下所示:
Snip20210730_58.png

  • 類的定義與結構體的類似,但編譯器并沒有為類自動生成可以傳入成員值的初始化器;
class PointXY{
    var x: Int = 0
    var y: Int = 0
}

import Foundation
//調用
var pointXY = PointXY()
  • 因為成員x與y有默認值,所以編譯器自動生成了無參的初始化器;
  • 若沒有默認值,那么編譯器不會生成任何初始化器;

結構體與類的本質區(qū)別

  • 結構體是值類型,枚舉也是值類型;
  • 類是引用類型,指針類型;
struct Point {
    var x: Int = 3
    var y: Int = 4
}

class Size {
    var width: Int = 5
    var height: Int = 6
}

import Foundation

var point = Point()
var size = Size()
Snip20210731_59.png
  • 結構體變量point是數值類型,在棧區(qū)分配內存空間,其棧地址為0x1000083B0,然后后面的16個字節(jié)存儲的是其成員x,y的值;
  • Size類的實例對象size是指針類型,size是指針變量,,在棧區(qū)分配內存空間,占8個字節(jié),其棧地址為0x1000083C0,其內存地址中存儲的是實例對象的內存地址即0x100657540,x/4gx 0x100657540可以獲取實例對象的內容,打印出4個8字節(jié)的內容,可以看到后面的16個字節(jié)存儲的是其成員width,height的值;
值類型
  • 值類型賦值給let,var或者函數傳參,是直接將所有的內容拷貝一份,屬于深拷貝;
struct Point {
    var x: Int
    var y: Int
}

func testZLX() -> Void {
    
    var point = Point(x: 10, y: 20)
    var point1 = point

    point1.x = 11
    point1.y = 22

    print(point.x)
    print(point.y)
}

import Foundation

testZLX()
  • 當斷點停在var point = Point(x: 10, y: 20)所在行,查看匯編代碼如下:
    0x100003040 <+0>:   pushq  %rbp
    0x100003041 <+1>:   movq   %rsp, %rbp
    0x100003044 <+4>:   subq   $0x90, %rsp
    0x10000304b <+11>:  xorps  %xmm0, %xmm0
    0x10000304e <+14>:  movaps %xmm0, -0x10(%rbp)
    0x100003052 <+18>:  movaps %xmm0, -0x20(%rbp)
    0x100003056 <+22>:  movl   $0xa, %edi
    0x10000305b <+27>:  movl   $0x14, %esi
->  0x100003060 <+32>:  callq  0x100003030               ; Swift09_枚舉的內存布局.Point.init(x: Swift.Int, y: Swift.Int) -> Swift09_枚舉的內存布局.Point at main.swift:32
    0x100003065 <+37>:  movq   %rax, -0x10(%rbp)
    0x100003069 <+41>:  movq   %rdx, -0x8(%rbp)
    0x10000306d <+45>:  movq   %rax, -0x20(%rbp)
    0x100003071 <+49>:  movq   %rdx, -0x18(%rbp)
    0x100003075 <+53>:  movq   $0xb, -0x20(%rbp)
    0x10000307d <+61>:  movq   $0x16, -0x18(%rbp)
    0x100003085 <+69>:  movq   0xf7c(%rip), %rcx         ; (void *)0x00007fff80cc5020: type metadata for Any
  • movl $0xa, %edi是將10存入edi寄存器;
  • movl $0x14, %esi是將20存入esi寄存器;
  • 然后進入初始化器,匯編如下:
Swift09_枚舉的內存布局`Point.init(x:y:):
->  0x100003030 <+0>:  pushq  %rbp
    0x100003031 <+1>:  movq   %rsp, %rbp
    0x100003034 <+4>:  movq   %rdi, %rax
    0x100003037 <+7>:  movq   %rsi, %rdx
    0x10000303a <+10>: popq   %rbp
    0x10000303b <+11>: retq   
  • movq %rdi, %rax將寄存器rdi中的值存入rax寄存器,即將10存入rax寄存器;
  • movq %rsi, %rdx將寄存器rsi中的值存入rdx寄存器,即將20存入rdx寄存器;
  • 執(zhí)行完初始化器方法,緊接著執(zhí)行以下指令:
  • movq %rax, -0x10(%rbp) 將rax寄存器中的值10,寫入內存地址(rbp - 0x10)中;
  • movq %rdx, -0x8(%rbp)將rdx寄存器中的值20,寫入內存地址(rbp - 0x8)中;
  • (rbp - 0x10)本質就是變量point的內存地址;
  • movq %rax, -0x20(%rbp) 將rax寄存器中的值10,寫入內存地址(rbp - 0x20)中;
  • movq %rdx, -0x18(%rbp)將rdx寄存器中的值20,寫入內存地址(rbp - 0x18)中;
  • (rbp - 0x18)本質就是變量point1的內存地址;
  • 從這里可以看出point與point1是兩份不同的內存地址,從而證明了值類型的傳遞屬于深拷貝;
  • 那么更改point1的成員值,不會影響到point;

值類型的賦值操作

import Foundation

var str1 = "123"
var str2 = str1
str2.append("456")
print("\(str1) -- \(str2)") //123 --123456

var arr1 = [1,2,3]
var arr2 = arr1
arr2.append(4)
print(arr1) //[1,2,3]
print(arr2) //[1,2,3,4]

var dic1 = ["name":"li","age":"10"]
var dic2 = dic1
dic2["height"] = "175"
print(dic1) //["name": "li", "age": "10"]
print(dic2) //["name": "li", "age": "10", "height": "175"]
  • String,Array,Dictionary都是值類型,則值傳遞時都是深拷貝,所以更改其中一個變量的內容,不會影響到副本中的內容;
  • 在Swift標準庫中,為了提升性能,String,Array,Dictionary,Set采用了Copy On Write技術,即只有當在真正改寫內容時才會進行深拷貝,否則本體與副本使用同一塊內存;
引用類型
  • 引用賦值給let,var或者給函數傳參,是將內存地址(引用)拷貝一份,屬于淺拷貝;
class Size {
    var width: Int = 3
    var height: Int = 4
}

import Foundation

var size1 = Size()
var size2 = size1
  • 原理如下圖所示:
Snip20210731_60.png
值類型,引用類型的let
struct Point {
    var x: Int
    var y: Int
}

class Size {
    var width: Int
    var height: Int
    init(width: Int,height: Int) {
        self.width = width
        self.height = height
    }
}
Snip20210731_61.png
  • let point是常量值類型,即point的值不可以修改;
  • let size是常量指針,即size的值不可以修改,也就是size的指向不可以修改,但其指向的對象可以修改;
對象申請堆內存空間的過程
  • 在Swift中,創(chuàng)建實例對象,需要向堆申請內存空間,其詳細過程如下所示:
  • Class.__allocating_init()
  • libSwiftCore.dylib:swift_allocObject
  • libSwiftCore.dylib:swift_slowAlloc
  • libsystem_malloc.dylib:malloc
  • 在Mac,iOS中maclloc分配堆內存空間函數,采用16字節(jié)內存對齊的算法;
  • class_getInstanceSize()函數是獲取實例對象占用的內存大小,采用的是8字節(jié)內存對齊算法;
import Foundation

func TestInstanceSize() -> Void {
    class Point {
        var x: Int = 100 //8
        var y: Int = 200 //8
        var origin: Bool = false //1
        //再加上前面的16個字節(jié) 總共實際會占用33個字節(jié)
    }
    let point = Point()
    //內存對齊之后的內存大小 8字節(jié)對齊
    print(class_getInstanceSize(type(of: point))) //40
    print(class_getInstanceSize(Point.self)) //40
    //maclloc申請堆空間內存 會采用16字節(jié)對齊,
    //所以最終會分配48個字節(jié)
}

TestInstanceSize()
  • point實例對象實際占用的內存大小為33個字節(jié);
嵌套類型
struct Test {
    enum Rank : Int {
        case two = 2,three,four
        case J,Q,K
    }
}

import Foundation

print(Test.Rank.two.rawValue) //2
  • 結構體中嵌套枚舉類型;
枚舉,結構體,類都可以定義方法
  • 一般把定義在枚舉,結構體,類內部的函數,叫做方法;
class Point {
    var x: Int = 100 //8
    var y: Int = 200 //8
    var origin: Bool = false //1
    func show() -> Void {
        print("Point class -- show")
    }
}

struct Size {
    var width: Int
    var height: Int
    func show() -> Void {
        print("Size struct -- show")
    }
}

enum Grade : Int {
    case perfect = 1,great,good,bad
    func show() -> Void {
        print("Grade enum -- show")
    }
}

import Foundation

var point = Point()
point.show()

var size = Size(width: 100, height: 200)
size.show()

var grade = Grade.great
grade.show()
  • 調試結果如下:
Snip20210731_62.png
  • 方法是存放在代碼段,是不會占用實例對象的內存空間;
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容