淺析Swift中struct與class的區(qū)別(匯編角度底層分析)
概述
相對(duì)Objective-C, Swift使用結(jié)構(gòu)體Struct的比例大大增加了,其中Int, Bool,以及String,Array等底層全部使用Struct來定義!在Swift中結(jié)構(gòu)體不僅可以定義成員變量(屬性),還可以定義成員方法,和類比較相似,都是具有定義和使用屬性,方法以及初始化器等面向?qū)ο筇匦?但是結(jié)構(gòu)體是不具有繼承性,不具備運(yùn)行時(shí)強(qiáng)制類型轉(zhuǎn)換的以及引用計(jì)數(shù)等能力的!
下面來從匯編角度分析struct與class的區(qū)別!
基本知識(shí)
1、結(jié)構(gòu)體
自動(dòng)初始化器
在63行的調(diào)用中可以傳入所有的成員值,用以初始化所有成員(存儲(chǔ)屬性, Stored Property)

在Struct Date定義中,并沒有出現(xiàn)init初始化方法,但是發(fā)現(xiàn)Date會(huì)自動(dòng)出現(xiàn)填入成員值的初始化方法
結(jié)論所有結(jié)構(gòu)體都會(huì)有一個(gè)編譯器自動(dòng)生成的初始化器(initializer,構(gòu)造器,構(gòu)造方法),編譯器會(huì)根據(jù)情況,可能會(huì)為結(jié)構(gòu)體生成多個(gè)初始化器,但是宗旨是:保證所有成員都有初始值
舉例1
下面四個(gè)初始化器,第一個(gè)初始化器之后保證了x,y都有值,滿足了上面說的保證所有成員都有初始值

p1,p2,p3都不能操作成功,因?yàn)椴荒鼙WC全部成員值都有值
通過上面的舉例,編譯器主動(dòng)生成了一個(gè)初始化器,用于接受成員值x,y的初始化器,其他不會(huì)主動(dòng)生成
舉例2
下面四個(gè)初始化器,第一個(gè)第二個(gè)p0,p1保證了x,y都有值,因?yàn)閤定義的時(shí)候賦值為0了,保證了成員值都有初始化值

p2,p3都不能操作成功,因?yàn)椴荒鼙WC全部成員值都有值
通過舉例2,編譯器主動(dòng)生成了兩個(gè)初始化器,用于接受x,y以及單獨(dú)接受y即可,其他的初始化器不會(huì)生成
舉例3
下面成員值在定義的時(shí)候就已經(jīng)給定了初始化值,已經(jīng)保證了所有成員值肯定會(huì)有初始化值

所以四個(gè)初始化器都可以,編譯器會(huì)自動(dòng)生成四個(gè)初始化器
舉例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? 相當(dāng)于將nil 賦值給x,所以上面四個(gè)都是可以編譯通過的 可選項(xiàng)都有個(gè)默認(rèn)值nil

自定義初始化器
一旦在定義結(jié)構(gòu)體的時(shí)候自定義好了初始化器,編譯器就不會(huì)再幫它自動(dòng)生成其他初始化器
舉例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()
在定義成員值時(shí)并賦值了初始值,也自定義初始化器,所以編譯器就不會(huì)自動(dòng)生成其他初始化器

看下這兩種初始化有何區(qū)別?
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()
通過匯編來查看是否有區(qū)別,兩個(gè)一模一樣,都是下面
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
內(nèi)存結(jié)構(gòu)
看一下下面一個(gè)結(jié)構(gòu)體的內(nèi)存結(jié)構(gòu)

根據(jù)內(nèi)存地址查看

從上面的存儲(chǔ)可看到,三個(gè)屬性的存儲(chǔ)地址是相鄰的!!!
也可以通過封裝的Mems內(nèi)存類來直接查詢

2、類
類的定義和結(jié)構(gòu)體類似, 但是編譯器并沒有為類自動(dòng)生成可以傳入成員值的初始化器

上面class定義,知編譯器不會(huì)自動(dòng)生成可以傳入成員值的初始化器,因?yàn)槎x的x,y都具有初始化值,xcode還會(huì)自動(dòng)的生成無參的初始化值,如果x,y沒有初始化值,連無參的初始化器都不會(huì)調(diào)用成功!
上面如果改成struct修飾,就不會(huì)有任何的錯(cuò)誤
結(jié)論:
如果類的所有成員都在定義的時(shí)候制定了初始值,編譯器會(huì)為類生成無參的初始化器
區(qū)別
1. 結(jié)構(gòu)體是值類型(枚舉也是值類型), 類是引用類型(指針類型)
class Size {
var width = 1
var height = 2
}
struct Point {
var x = 3
var y = 4
}
func test() {
var size = Size()
var point = Point()
}
對(duì)于上面的代碼,point為值類型,如果值類型在函數(shù)里面定義,就放在??臻g,point里面有x,y共有16個(gè)字節(jié),假設(shè)起始地址為0x10000,而Size對(duì)象是引用類型,size指針變量存放在棧空間中,存放的是地址(指針類型占用8個(gè)字節(jié)),地址指向的為堆空間,堆空間的大小為32個(gè)字節(jié),內(nèi)存結(jié)構(gòu)大致如

而size對(duì)象內(nèi)存則放在堆空間,結(jié)構(gòu)結(jié)構(gòu)如下

進(jìn)行驗(yàn)證(如果匯編里面沒有出現(xiàn)alloc,malloc等詞,基本就不是堆空間)

發(fā)現(xiàn)size指針變量和point變量地址挨著很近!!!
進(jìn)一步,我們想觀看size指針變量指向的堆空間的內(nèi)容和指針地址,通過Mems工具類查看

對(duì)于上面的補(bǔ)充
對(duì)于類創(chuàng)建的對(duì)象都是是堆空間,只是類對(duì)象的指針變量可能會(huì)在不同的地方,如上面size是在函數(shù)里面,size指針變量放在棧里面,但是Size對(duì)象就是堆空間,不存在其他的,如果創(chuàng)建size對(duì)象在函數(shù)外創(chuàng)建,則size指針變量就放在了全局區(qū)里面
拓展
值類型: 值類型賦值給var,let或者給函數(shù)傳參, 是直接將所有內(nèi)容拷貝一份,類似于對(duì)文件進(jìn)行copy,paste操作,產(chǎn)生了新的文件副本,屬于深拷貝(deep copy)

匯編指令小技巧

引用類型: 引用賦值給var,let或者給函數(shù)傳參, 是將內(nèi)存地址拷貝一份,類似于制作一個(gè)文件的替身(快捷方式、鏈接)指向的是同一個(gè)文件,屬于淺拷貝(shallow copy)

上面可看出,s1, s2 都指向同一內(nèi)存,當(dāng)更改s2的值時(shí),s1也會(huì)更改掉,此為淺拷貝的應(yīng)用!!!
總結(jié)
到此這篇關(guān)于Swift--struct與class的區(qū)別(匯編角度底層分析)的文章就介紹到這了,更多相關(guān)swift struct與class內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Swift算法實(shí)現(xiàn)逐字翻轉(zhuǎn)字符串的方法示例
大家都知道翻轉(zhuǎn)字符串在字符串算法中算是比較常見的,下面這篇文章主要介紹了Swift算法實(shí)現(xiàn)逐字翻轉(zhuǎn)字符串的方法,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考借鑒,下面來一起看看吧。2017-03-03
Swift中通知中心(NotificationCenter)的使用示例
這篇文章主要給大家介紹了關(guān)于Swift中通知中心(NotificationCenter)使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
關(guān)于Swift 4.1中的Codable改進(jìn)詳解
這篇文章主要給大家介紹了關(guān)于Swift 4.1中的Codable改進(jìn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02
Swift的開發(fā)環(huán)境搭建以及基本語法詳解
這篇文章主要介紹了Swift的開發(fā)環(huán)境搭建以及基本語法詳解,是Swift入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11
使用Swift實(shí)現(xiàn)iOS App中解析XML格式數(shù)據(jù)的教程
這篇文章主要介紹了使用Swift實(shí)現(xiàn)iOS App中解析XML格式數(shù)據(jù)的教程,講到了iOS中提供的NSXMLParser和NSXMLParserDelegate兩個(gè)API的用法,需要的朋友可以參考下2016-04-04

