概述

相对Objective-C, Swift使用结构体Struct的比例大大增加了,其中Int, Bool,以及String,Array等底层全部使用Struct来定义!在Swift中结构体不仅可以定义成员变量(属性),还可以定义成员方法,和类比较相似,都是具有定义和使用属性,方法以及初始化器等面向对象特性,但是结构体是不具有继承性,不具备运行时强制类型转换的以及引用计数等能力的!

下面来从汇编角度分析struct与class的区别!

基本知识

1、结构体

自动初始化器

在63行的调用中可以传入所有的成员值,用以初始化所有成员(存储属性, Stored Property)

在Struct Date定义中,并没有出现init初始化方法,但是发现Date会自动出现填入成员值的初始化方法

结论所有结构体都会有一个编译器自动生成的初始化器(initializer,构造器,构造方法),编译器会根据情况,可能会为结构体生成多个初始化器,但是宗旨是:保证所有成员都有初始值

举例1 

下面四个初始化器,第一个初始化器之后保证了x,y都有值,满足了上面说的保证所有成员都有初始值

p1, p2, p3都不能操作成功,因为不能保证全部成员值都有值

通过上面的举例,编译器主动生成了一个初始化器,用于接受成员值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: , y: )
var p1 = Point(y: )
var p2 = Point(x: )
var p3 = Point()

定义var x: Int? 相当于将nil 赋值给x,所以上面四个都是可以编译通过的  可选项都有个默认值nil

自定义初始化器

一旦在定义结构体的时候自定义好了初始化器,编译器就不会再帮它自动生成其他初始化器

举例1

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

在定义成员值时并赋值了初始值,也自定义初始化器,所以编译器就不会自动生成其他初始化器

看下这两种初始化有何区别?

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

通过汇编来查看是否有区别,两个一模一样,都是下面

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

内存结构

看一下下面一个结构体的内存结构 

根据内存地址查看

 从上面的存储可看到,三个属性的存储地址是相邻的!!!

也可以通过封装的Mems内存类来直接查询

2、类

类的定义和结构体类似, 但是编译器并没有为类自动生成可以传入成员值的初始化器

上面class定义,知编译器不会自动生成可以传入成员值的初始化器,因为定义的x,y都具有初始化值,xcode还会自动的生成无参的初始化值,如果x,y没有初始化值,连无参的初始化器都不会调用成功!

上面如果改成struct修饰,就不会有任何的错误

结论:

如果类的所有成员都在定义的时候制定了初始值,编译器会为类生成无参的初始化器 

 

区别

 1. 结构体是值类型(枚举也是值类型), 类是引用类型(指针类型)

2. 值类型(结构体,枚举)不支持继承,类支持继承

class Size {
var width =
var height =
}
struct Point {
var x =
var y =
}
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也会更改掉,此为浅拷贝的应用!!!

Swift--struct与class的区别(汇编角度底层分析)的更多相关文章

  1. Swift 枚举-从汇编角度看枚举内存结构

    一.基本使用 先看枚举的几种使用(暂不要问,看看是否都能看懂,待会会逐一讲解) 1.操作一 简单使用 //第一种方式 enum Direction { case east case west case ...

  2. C#中struct和class的区别详解

    本文详细分析了C#中struct和class的区别,对于C#初学者来说是有必要加以了解并掌握的. 简单来说,struct是值类型,创建一个struct类型的实例被分配在栈上.class是引用类型,创建 ...

  3. C#中struct和class的区别详解 (转载)

    本文详细分析了C#中struct和class的区别,对于C#初学者来说是有必要加以了解并掌握的. 简单来说,struct是值类型,创建一个struct类型的实例被分配在栈上.class是引用类型,创建 ...

  4. struct与class的区别

    C++中的struct是对C中struct的扩充,它已经不再只是一个包含不同数据类型的数据结构,因为其扩充了太多功能.总的来说,C++中struct和class极其相似,比如,struct能包含成员函 ...

  5. C++学习基础十三——struct和class的区别

    来自:http://blog.sina.com.cn/s/blog_48f587a80100k630.html C++中的struct是对C中struct进行了扩展,它不单是一个包含不同数据类型的数据 ...

  6. C语言函数调用过程,汇编角度查看

    C语言函数调用过程,汇编角度查看 把函数的参数按照调用约定压栈或者存储到寄存器中 调用要使用的函数,先把调用者的地址入栈,方便回来 跳转到函数 把函数使用到的一些寄存器压栈,避免修改寄存器的值 执行函 ...

  7. JVM系列之:从汇编角度分析Volatile

    目录 简介 重排序 写的内存屏障 非lock和LazySet 读的性能 总结 简介 Volatile关键字对熟悉java多线程的朋友来说,应该很熟悉了.Volatile是JMM(Java Memory ...

  8. u-boot源码汇编段简要分析

    Hi,大家好!我是CrazyCatJack,你们可以叫我CCJ或者疯猫.今天我给大家带来的是u-boot的源代码汇编段分析,以后还会给大家讲解后续的C代码,请持续关注哦^_^ 先简单说一下u-boot ...

  9. 从运维角度来分析mysql数据库优化的一些关键点【转】

    概述 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善. 1.数据库表设计 项目立项后,开发部根据产品部需求开发项目,开发工程师工作其中一部分 ...

随机推荐

  1. std::string和ctime之间的转换

    int year, month, day, hour, minute, second; string strTime: sscanf(strTime.c_str(), "%d-%d-%d % ...

  2. [LC] 1007. Minimum Domino Rotations For Equal Row

    In a row of dominoes, A[i] and B[i] represent the top and bottom halves of the i-th domino.  (A domi ...

  3. 吴裕雄--天生自然python学习笔记:python 创建和操作Firebase实时数据库

    Fire base 是专为移动开发提供的后端服务平台, Firebase 数据库与传统数据库不同,它不是用数据表来存储数据, 而是用 Key . Value 的字典型结构来存储数据,所以它不仅 是轻量 ...

  4. hdu 2586 How far away ?(LCA模板)(倍增法)

    在dfs的过程中维护三个数组: deep[i],表示i点在树中的深度: grand[x][i],表示x的第2^i个祖先的节点编号: dis[x][i],表示x到它2^i祖 #include<io ...

  5. GSON转换成Long型变为科学计数法及时间格式转换异常的解决方案

    直接上工具类了,简单实用 public class GsonUtils { private static Gson gson = null; static { if (gson == null) { ...

  6. 二:MySQL的操作

    1:创建数据库 create database  bjpowernode ; 2:使用数据库 use bjpowernode; 3:导入数据库文件sql source  然后把SQL文件拖过来就可以了 ...

  7. 吴裕雄--天生自然python学习笔记:pandas模块用 dataframe.loc 通过行、列标题读取数据

    用 df.va lue s 读取数据的前提是必须知道学生及科目的位置,非常麻烦 . 而 df.loc 可直接通过行.列标题读取数据,使用起来更为方便 . 使用 df.loc 的语法为: 行标题或列标题 ...

  8. LeetCode No.76,77,78

    No.76 MinWindow 最小覆盖子串 题目 给你一个字符串 S.一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串. 示例 输入: S = "ADOBECODEB ...

  9. The Pomodoro Technique

    目录 简介 What to solve How to use Some applications 自我总结 结束语 简介 番茄工作法是简单易行的时间管理方法,是由弗朗西斯科·西里洛于1992年创立的一 ...

  10. OpenGL 保存bmp图像

    今天我们先简单介绍Windows中常用的BMP文件格式,然后讲OpenGL的像素操作.虽然看起来内容可能有点多,但实际只有少量几个知识点,如果读者对诸如”显示BMP图象”等内容比较感兴趣的话,可能不知 ...