浅析Swift中struct与class的区别(汇编角度底层分析)

概述

相对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: 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内存类来直接查询

2、类

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

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

总结

到此这篇关于Swift--struct与class的区别(汇编角度底层分析)的文章就介绍到这了,更多相关swift struct与class内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Swift中的类class与结构体struct体学习笔记

    一.引言 Swift中的类与结构体十分相似,和Objective-C不同的事,Swift中的结构体不仅可以定义属性,也可以像类一样为其定义方法. Swift中的类与结构体有如下相似点: 1.定义属性来存储值. 2.定义函数来提供功能. 3.通过定义下标语法使用下标的方式取值. 4.定义构造方法来对其进行初始化. 5.通过扩展来在原始基础上添加功能. 6.通过协议来定义实现标准. 当然类和结构体也有许多不同点,下面这些功能是类独有的,结构体没有: 1.通过继承来创建类的子类. 2.在运行时允许对类

  • C++结构体struct和类class区别详解

    之前因为都在忙着毕业的开题答辩与投稿论文的事宜,一直没有时间更新这个系列的文章.师弟看了上一篇雾中风景的文章,希望我继续把这个系列的文章写下去.坦白说,C++的特性很多,这也不是教学指南的文章,我会选取一些自己在学习C++过程之中值得探讨的问题和大家聊一聊,来抛砖引玉.好的,今天先放点开胃菜,和大家聊聊struct与class关键字. 1.struct关键字: 在C++语言作为C语言的一个超集,是兼容C语言的所有语法规则的.C语言是我学习的第一门编程语言,我自然对于其中的语法规则十分熟悉,C语言

  • C#中struct和class的区别详解

    本文详细分析了C#中struct和class的区别,对于C#初学者来说是有必要加以了解并掌握的. 简单来说,struct是值类型,创建一个struct类型的实例被分配在栈上.class是引用类型,创建一个class类型实例被分配在托管堆上.但struct和class的区别远不止这么简单. 概括来讲,struct和class的不同体现在: ● 类是引用类型,struct是值类型 ● 在托管堆上创建类的实例,在栈上创建struct实例 ● 类实例的赋值,赋的是引用地址,struct实例的赋值,赋的是

  • C++中关键字Struct和Class的区别

    Struct和Class的区别 今天这篇博文主要讲解在C++中关键字struct和class的区别.这篇博文,将会系统的将这两个关键字的不同面进行详细的讲解. 从语法上来讲,class和struct做类型定义时只有两点区别: 1.默认继承权限,如果不指定,来自class的继承按照private继承处理,来自struct的继承按照public继承处理: 2.成员的默认访问权限.class的成员默认是private权限,struct默认是public权限.以上两点也是struct和class最基本的

  • 深入C++中struct与class的区别分析

    一.C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能.struct能包含成员函数吗?   能!struct能继承吗?  能!!struct能实现多态吗?   能!!! 最本质的一个区别就是默认的访问控制,体现在两个方面:1)默认的继承访问权限.struct是public的,class是private的.   写如下的代码: 复制代码 代码如下: struct A{  char a;}:struct B : A{  cha

  • 浅析Swift中struct与class的区别(汇编角度底层分析)

    概述 相对Objective-C, Swift使用结构体Struct的比例大大增加了,其中Int, Bool,以及String,Array等底层全部使用Struct来定义!在Swift中结构体不仅可以定义成员变量(属性),还可以定义成员方法,和类比较相似,都是具有定义和使用属性,方法以及初始化器等面向对象特性,但是结构体是不具有继承性,不具备运行时强制类型转换的以及引用计数等能力的! 下面来从汇编角度分析struct与class的区别! 基本知识 1.结构体 自动初始化器 在63行的调用中可以传

  • 浅析Android中getWidth()和getMeasuredWidth()的区别

    结论:getMeasuredWidth()获取的是view原始的大小,也就是这个view在XML文件中配置或者是代码中设置的大小.getWidth()获取的是这个view最终显示的大小,这个大小有可能等于原始的大小也有可能不等于原始大小. 1.getMeasuredWidth 从源码上来看,getMeasuredWidth()获取的是mMeasuredWidth的这个值.这个值是一个8位的十六进制的数字,高两位表示的是这个measure阶段的Mode的值,具体可以查看MeasureSpec的原理

  • 浅析java中Pair和Map的区别

    在这篇文章中,我们讨论了一个非常有用的编程概念,配对(Pair).配对提供了一种方便方式来处理简单的键值关联,当我们想从方法返回两个值时特别有用. 在核心Java库中可以使用配对(Pair)的实现.除此之外,某些第三方库,比如Apache Commons和Vavr,已经在各自的api中公开了这个功能. 核心java配对实现 Pair类 Pair类在javafx.util 包中,类构造函数有两个参数,键及对应值: Pair<Integer, String> pair = new Pair<

  • 浅析PHP中json_encode与json_decode的区别

    一.json_encode() 对变量进行JSON编码 语法:json_encode($value[,$options=0]) 注意:  1.$value为要编码的值,且该函数只对UTF8编码的数据有效:              2.options:由以下常量组成的二进制掩码:JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS,JSON_NUMERIC_CHECK,JSON_PRETTY_PRINT, JSON_UNESCAPED_

  • C#中struct与class的区别详解

    目录 1.最大的区别 2.struct成员无法被声明为protected. 3.struct是隐式的sealed类 4.struct中无法重载默认构造函数 5.关于对象的初始化 6.结构体没有析构函数,也无法自己去给结构体定义一个析构函数 1.最大的区别 struct类型定义的变量是值类型,class定义的变量是引用类型.因此struct类型定义的对象是分配在栈上面的,而class定义的对象是分配在堆上的. 2.struct成员无法被声明为protected. 举例: struct Test1

  • Swift中 !和 ?的区别及使用

    相信大家在学习和使用Swift的时候,肯定会被 ! 和  ? 搞疯过, 纠结这两个符号到底是个什么鬼 ?鬼知道什么时候使用!,什么时候使用? 下面就说一下! 和 ? 区别以及该怎么使用! ? 和 ! 到底是个啥 ? 和 ! 其实分别是Swift语言中对一种可选类型( Optional) 操作的语法糖. 那可选类型是干什么的呢? Swift中是可以声明一个没有初始值的属性, Swift中引入了可选类型(Optional)来解决这一问题.它的定义是通过在类型生命后加加一个 ? 操作符完成的. 例如:

  • 浅析Java中Runnable和Thread的区别

    线程的起动并不是简单的调用了你的RUN方法,而是由一个线程调度器来分别调用你的所有线程的RUN方法, 我们普通的RUN方法如果没有执行完是不会返回的,也就是会一直执行下去,这样RUN方法下面的方法就不可能会执行了,可是线程里的RUN方法却不一样,它只有一定的CPU时间,执行过后就给别的线程了,这样反复的把CPU的时间切来切去,因为切换的速度很快,所以我们就感觉是很多线程在同时运行一样. 你简单的调用run方法是没有这样效果的,所以你必须调用Thread类的start方法来启动你的线程.所以你启动

  • 浅析Vue中method与computed的区别

    在new Vue的配置参数中的computed和methods都可以处理大量的逻辑代码,但是什么时候用哪个属性,要好好区分一下才能做到正确的运用vue. computed称为计算属性,顾名思义,计算就要返回一个计算的结果,所以,当我们要处理大量的逻辑,但是最后要取得最后的结果的时候可以用computed: 为了说明method与computed的区别,在此我想先来看看computed属性在vue官网中的说法:模板内的表达式是非常便利的,但是它们实际上只用于简单的运算.在模板中放入太多的逻辑会让模

随机推荐