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也会更改掉,此为浅拷贝的应用!!!

相关文章
相关标签/搜索