文章首發於個人我的博客html
類的定義和結構體相似,但編譯器並無爲類自動生成能夠傳入成員值的初始化器 eg 以下代碼不會報錯git
struct Point {
var x: Int = 0
var y: Int = 0
}
let p1 = Point()
let p2 = Point(x: 10, y: 20)
let p3 = Point(x: 10)
let p4 = Point(y: 20)
複製代碼
可是若是改爲了類就不能編譯經過github
class Point {
var x: Int = 0
var y: Int = 0
}
let p1 = Point()
let p2 = Point(x: 10, y: 20)//報錯Argument passed to call that takes no arguments
let p3 = Point(x: 10)//報錯Argument passed to call that takes no arguments
let p4 = Point(y: 20)//報錯Argument passed to call that takes no arguments
複製代碼
struct Point {
var x: Int = 0
var y: Int = 0
}
struct Point {
var x: Int
var y: Int
init() {
x=0
y=0
}
}
複製代碼
eg:咱們有以下的結構體point 和類 size編程
// 類size
class Size {
var width = 1
var height = 2
}
// 類 Point
struct Point {
var x = 3
var y = 4
}
// 變量 size 接收類Size
var size = Size()
// 變量 point 接收結構體point
var point = Point()
複製代碼
咱們假設 執行完test() 以後,point的內存地址爲 0x10000 size的內存地址爲 0x10010 能夠用一幅圖來表示swift
上圖表示,point是值拷貝,直接把 3 和 4 放在了point對應的內存中,而 指針變量size是引用拷貝,是放了 Size() 的指針 0x90000 ,而對應的 堆空間 0x90000中才真正的存放1和2,固然了,前面有16個字節,存放了類的信息,和引用計數,由於Swift和OC同樣使用的引用計數來內存管理的,因此Size對象用了32個字節bash
代碼以下app
unc test1(){
// 類size
class Size {
var width = 1
var height = 2
}
// 類 Point
struct Point {
var x = 3
var y = 4
}
// 變量 point 接收結構體point
var point = Point()
// 變量 size 接收類Size
var size = Size()
}
test1()
複製代碼
上面的代碼,先在 var point = Point() 處打斷點函數
testSwift`__allocating_init() in Size #1 in test1():
0x100001030 <+0>: pushq %rbp
0x100001031 <+1>: movq %rsp, %rbp
0x100001034 <+4>: pushq %r13
0x100001036 <+6>: subq $0x18, %rsp
0x10000103a <+10>: xorl %eax, %eax
0x10000103c <+12>: movl %eax, %edi
-> 0x10000103e <+14>: callq 0x100001250 ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated>
0x100001043 <+19>: movl $0x20, %ecx
0x100001048 <+24>: movl %ecx, %esi
0x10000104a <+26>: movl $0x7, %ecx
0x10000104f <+31>: movl %ecx, %edi
0x100001051 <+33>: movq %rdi, -0x10(%rbp)
0x100001055 <+37>: movq %rax, %rdi
0x100001058 <+40>: movq -0x10(%rbp), %rax
0x10000105c <+44>: movq %rdx, -0x18(%rbp)
0x100001060 <+48>: movq %rax, %rdx
0x100001063 <+51>: callq 0x100005046 ; symbol stub for: swift_allocObject
0x100001068 <+56>: movq %rax, %r13
0x10000106b <+59>: callq 0x100001e30 ; init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
0x100001070 <+64>: addq $0x18, %rsp
0x100001074 <+68>: popq %r13
0x100001076 <+70>: popq %rbp
0x100001077 <+71>: retq
複製代碼
從工具
0x10000103e <+14>: callq 0x100001250 ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated>
複製代碼
處執行lldb命令 si 跟蹤進去性能
testSwift`init() in Point #1 in test1():
-> 0x100001030 <+0>: pushq %rbp
0x100001031 <+1>: movq %rsp, %rbp
0x100001034 <+4>: xorps %xmm0, %xmm0
0x100001037 <+7>: movaps %xmm0, -0x10(%rbp)
0x10000103b <+11>: movq $0x3, -0x10(%rbp)
0x100001043 <+19>: movq $0x4, -0x8(%rbp)
0x10000104b <+27>: movl $0x3, %eax
0x100001050 <+32>: movl $0x4, %ecx
0x100001055 <+37>: movl %ecx, %edx
0x100001057 <+39>: popq %rbp
0x100001058 <+40>: retq
複製代碼
能夠看到賦值操做 直接是把 $0x3 和 $0x4 賦值給棧空間 (-0x10(%rbp) 和 -0x8(%rbp) )的,沒有調用malloc alloc 等方法,也就是沒有開闢堆空間
上面的代碼,先在 var size = Size() 處打斷點
0x100000fe0 <+0>: pushq %rbp
0x100000fe1 <+1>: movq %rsp, %rbp
0x100000fe4 <+4>: pushq %r13
0x100000fe6 <+6>: subq $0x28, %rsp
0x100000fea <+10>: movq $0x0, -0x10(%rbp)
0x100000ff2 <+18>: xorps %xmm0, %xmm0
0x100000ff5 <+21>: movaps %xmm0, -0x20(%rbp)
0x100000ff9 <+25>: xorl %eax, %eax
0x100000ffb <+27>: movl %eax, %edi
-> 0x100000ffd <+29>: callq 0x100001250 ; type metadata accessor for Size #1 in testSwift.test1() -> () at <compiler-generated>
0x100001002 <+34>: movq %rax, %r13
0x100001005 <+37>: movq %rdx, -0x28(%rbp)
0x100001009 <+41>: callq 0x100001030 ; __allocating_init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
0x10000100e <+46>: movq %rax, -0x10(%rbp)
0x100001012 <+50>: callq 0x100001080 ; init() -> Point #1 in testSwift.test1() -> () in Point #1 in testSwift.test1() -> () at main.swift:21
0x100001017 <+55>: movq %rax, -0x20(%rbp)
0x10000101b <+59>: movq %rdx, -0x18(%rbp)
0x10000101f <+63>: movq -0x10(%rbp), %rdi
0x100001023 <+67>: callq 0x1000050ac ; symbol stub for: swift_release
0x100001028 <+72>: addq $0x28, %rsp
0x10000102c <+76>: popq %r13
0x10000102e <+78>: popq %rbp
0x10000102f <+79>: retq
複製代碼
進入
0x100001009 <+41>: callq 0x100001030 ; __allocating_init() -> Size #1 in testSwift.test1() -> () in Size #1 in testSwift.test1() -> () at main.swift:15
複製代碼
一路跟蹤進入,最終來到了以下圖所示位置
也就是確實分配了堆空間,驗證了咱們前面的結論
Class.__allocating_init()
libswiftCore.dylib:_swift_allocObject_
libswiftCore.dylib:swift_slowAlloc
libsystem_malloc.dylib:malloc
複製代碼
eg:
class Point {
var x = 11
var test = true
var y = 22
}
var p = Point()
class_getInstanceSize(type(of: p)) // 40
class_getInstanceSize(Point.self) // 40
複製代碼
值類型賦值給var、let或者給函數傳參,是直接將全部內容拷貝一份
相似於對文件進行copy、paste操做,產生了全新的文件副本。屬於深拷貝(deep copy)
在Swift標準庫中,爲了提高性能,String、Array、Dictionary、Set採起了Copy On Write的技術
建議:不須要修改的,儘可能定義成let
// 類中定義方法
class Size {
var width = 10
var height = 10
func show() {
print("width=\(width), height=\(height)")
}
}
let s = Size()
s.show() // width=10, height=10
// 結構體中定義方法
struct Point {
var x = 10
var y = 10
func show() {
print("x=\(x), y=\(y)")
}
}
let p = Point()
p.show() // x=10, y=10
// 枚舉中定義方法
enum grade : Character {
case a = "a"
case b = "b"
func show() {
print("res is \(rawValue)")
}
}
let g = grade.a
g.show() // res is a
複製代碼
參考資料:
更多資料,歡迎關注我的公衆號,不定時分享各類技術文章。