Swift--struct與class的區別(彙編角度底層分析)

概述

相對Objective-C, Swift使用結構體Struct的比例大大增長了,其中Int, Bool,以及String,Array等底層所有使用Struct來定義!在Swift中結構體不只能夠定義成員變量(屬性),還能夠定義成員方法,和類比較類似,都是具備定義和使用屬性,方法以及初始化器等面向對象特性,可是結構體是不具備繼承性,不具有運行時強制類型轉換的以及引用計數等能力的!xcode

下面來從彙編角度分析struct與class的區別!函數

基本知識

一、結構體

自動初始化器工具

在63行的調用中能夠傳入全部的成員值,用以初始化全部成員(存儲屬性, Stored Property)spa

在Struct Date定義中,並無出現init初始化方法,可是發現Date會自動出現填入成員值的初始化方法3d

結論全部結構體都會有一個編譯器自動生成的初始化器(initializer,構造器,構造方法),編譯器會根據狀況,可能會爲結構體生成多個初始化器,可是宗旨是:保證全部成員都有初始值指針

舉例1 code

下面四個初始化器,第一個初始化器以後保證了x,y都有值,知足了上面說的保證全部成員都有初始值對象

 p1, p2, p3都不能操做成功,由於不能保證所有成員值都有值blog

經過上面的舉例,編譯器主動生成了一個初始化器,用於接受成員值x,y的初始化器,其餘不會主動生成繼承

舉例2

下面四個初始化器,第一個第二個p0,p1保證了x,y都有值,由於x定義的時候賦值爲0了,保證了成員值都有初始化值

 p2,p3都不能操做成功,由於不能保證所有成員值都有值

經過舉例2,編譯器主動生成了兩個初始化器,用於接受x,y以及單獨接受y便可,其餘的初始化器不會生成

舉例3

下面成員值在定義的時候就已經給定了初始化值,已經保證了全部成員值確定會有初始化值

 因此四個初始化器均可以,編譯器會自動生成四個初始化器

舉例4

下面代碼能編譯經過嘛?

struct Point {
    var x: Int?
    var y: Int?
}
var p0 = Point(x: 0, y: 10)
var p1 = Point(y: 0)
var p2 = Point(x: 0)
var p3 = Point()

定義var x: Int? 至關於將nil 賦值給x,因此上面四個都是能夠編譯經過的  可選項都有個默認值nil

自定義初始化器

一旦在定義結構體的時候自定義好了初始化器,編譯器就不會再幫它自動生成其餘初始化器

舉例1

struct Point {
    var x: Int = 0
    var y: Int = 0
    init(x: Int, y: Int) {
        self.x = x
        self.y = y }
}
var p0 = Point(x: 0, y: 10)
var p1 = Point(y: 0)
var p2 = Point(x: 0)
var p3 = Point()

在定義成員值時並賦值了初始值,也自定義初始化器,因此編譯器就不會自動生成其餘初始化器

看下這兩種初始化有何區別?

func testStruct() {
    struct Point {
        var x: Int = 0
        var y: Int = 0
    }
    var _ = Point()
}
testStruct()
func testStruct() {
    struct Point {
        var x: Int
        var y: Int
        init() {
            x = 0
            y = 0
        }
    }
    var _ = Point()
}
testStruct()

經過彙編來查看是否有區別,兩個如出一轍,都是下面

TestSwift`init() in Point #1 in testStruct():
->  0x100001940 <+0>:  pushq  %rbp
    0x100001941 <+1>:  movq   %rsp, %rbp
    0x100001944 <+4>:  xorps  %xmm0, %xmm0
    0x100001947 <+7>:  movaps %xmm0, -0x10(%rbp)
    0x10000194b <+11>: movq   $0x0, -0x10(%rbp)
    0x100001953 <+19>: movq   $0x0, -0x8(%rbp)
    0x10000195b <+27>: xorl   %eax, %eax
    0x10000195d <+29>: movl   %eax, %ecx
    0x10000195f <+31>: movq   %rcx, %rax
    0x100001962 <+34>: movq   %rcx, %rdx
    0x100001965 <+37>: popq   %rbp
    0x100001966 <+38>: retq

 

內存結構

看一下下面一個結構體的內存結構 

 根據內存地址查看

 從上面的存儲可看到,三個屬性的存儲地址是相鄰的!!!

也能夠經過封裝的Mems內存類來直接查詢

二、類

類的定義和結構體相似, 可是編譯器並無爲類自動生成能夠傳入成員值的初始化器

 上面class定義,知編譯器不會自動生成能夠傳入成員值的初始化器,由於定義的x,y都具備初始化值,xcode還會自動的生成無參的初始化值,若是x,y沒有初始化值,連無參的初始化器都不會調用成功!

上面若是改爲struct修飾,就不會有任何的錯誤

結論:

若是類的全部成員都在定義的時候制定了初始值,編譯器會爲類生成無參的初始化器 

 

區別

 1. 結構體是值類型(枚舉也是值類型), 類是引用類型(指針類型)

class Size {
    var width = 1
    var height = 2
}
struct Point {
    var x = 3
    var y = 4
}
func test() {
    var size = Size()
    var point = Point()
}

對於上面的代碼,point爲值類型,若是值類型在函數裏面定義,就放在棧空間,point裏面有x,y共有16個字節,假設起始地址爲0x10000,而Size對象是引用類型,size指針變量存放在棧空間中,存放的是地址(指針類型佔用8個字節),地址指向的爲堆空間,堆空間的大小爲32個字節,內存結構大體如

而size對象內存則放在堆空間,結構結構以下

 進行驗證(若是彙編裏面沒有出現alloc,malloc等詞,基本就不是堆空間)

 發現size指針變量和point變量地址挨着很近!!!

進一步,咱們想觀看size指針變量指向的堆空間的內容和指針地址,經過Mems工具類查看

 對於上面的補充

對於類建立的對象都是是堆空間,只是類對象的指針變量可能會在不一樣的地方,如上面size是在函數裏面,size指針變量放在棧裏面,可是Size對象就是堆空間,不存在其餘的,若是建立size對象在函數外建立,則size指針變量就放在了全局區裏面

 

拓展

值類型: 值類型賦值給var,let或者給函數傳參, 是直接將全部內容拷貝一份,相似於對文件進行copy,paste操做,產生了新的文件副本,屬於深拷貝(deep copy)

 彙編指令小技巧

引用類型: 引用賦值給var,let或者給函數傳參, 是將內存地址拷貝一份,相似於製做一個文件的替身(快捷方式、連接)指向的是同一個文件,屬於淺拷貝(shallow copy)

 上面可看出,s1, s2 都指向同一內存,當更改s2的值時,s1也會更改掉,此爲淺拷貝的應用!!!

相關文章
相關標籤/搜索