文章首发于个人我的博客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
复制代码
参考资料:
更多资料,欢迎关注我的公众号,不定时分享各类技术文章。