相同点
定义存储值的属性
定义方法
定义下标以使用下标语法提供对其值的访问
定义初始化器
使用 extension 来拓展功能
遵循协议来提供某种功能
不同点
类有继承的特性,而结构体没有
类型转换使您能够在运行时检查和解释类实例的类型
类有析构函数用来释放其分配的资源
引用计数允许对一个类实例有多个引用
值类型与引用类型
Swift 中所有的基本类型——整数,浮点数,布尔量,字符串,数组和字典——都是值类型,并且都以结构体的形式在后台实现。Swift中的结构体和枚举也是值类型。
不同于值类型,在引用类型被赋值到一个常量,变量或者本身被传递到一个函数的时候它是不会被拷贝的。相对于拷贝,变量及被赋值的对象使用的是同一个对现存实例的引用。
类与结构体实例
结构体和类两者都能使用初始化器语法来生成新的实例。初始化器语法最简单的是在类或结构体名字后面接一个空的圆括号,例如 Person()或者 Teacher()。这样就创建了一个新的类或者结构体的实例,任何属性都被初始化为它们的默认值。
初始化器
所有的结构体都有一个自动生成的成员初始化器,你可以使用它来初始化新结构体实例的成员属性。新实例属性的初始化值可以通过属性名称传递到成员初始化器中:
类编译器默认不会自动提供成员初始化器,所以在类中必须提供指定的初始化器,同时也可以为当前类提供便捷初始化器(便捷初始化器必须从相同的类里调用另一个初始化器)
指定初始化器必须保证在向上委托给父类初始化器之前,其所在类引入的所有属性 都要初始化完成。
指定初始化器必须先向上委托父类初始化器,然后才能为继承的属性设置新值。如 果不这样做,指定初始化器赋予的新值将被父类中的初始化器所覆盖
便捷初始化器必须先委托同类中的其它初始化器,然后再为任意属性赋新值(包括 同类里定义的属性)。如果没这么做,便捷构初始化器赋予的新值将被自己类中其 它指定初始化器所覆盖。
初始化器在第一阶段初始化完成之前,不能调用任何实例方法、不能读取任何实例 属性的值,也不能引用 self 作为值。
引用类型和值类型存储位置
引用类型和值类型还有一个最直观的区别就是存储的位置不同:一般情况,值类型存储的在栈上,引用类型存储在堆上。
栈区(stack):局部变量和函数运行过程中的上下文
Heap: 存储所有对象
Global: 存储全局变量;常量;代码区
Segment & Section: Mach-O 文件有多个段( Segment ),每个段有不同的功能。然后每
个段又分为很多小的 Section
指针
如果你有过 C,C++ 或者 Objective-C 的经验,你可能知道这些语言使用了指针来引用内存中的地址。一个 Swift 的常量或者变量指向某个实例的引用类型和 C 中的指针类似,但是这并不是直接指向内存地址的指针,也不需要你书写星号( *)来明确你建立了一个引用。相反,这些引用被定义得就像 Swift 中其他常量或者变量一样。
字符串,数组和字典的赋值与拷贝行为
Swift 的 String, Array和 Dictionary类型是作为结构体来实现的,这意味着字符串,数组和字典在它们被赋值到一个新的常量或者变量,亦或者它们本身被传递到一个函数或方法中的时候,其实是传递了拷贝。
这种行为不同于基础库中的 NSString, NSArray和 NSDictionary,它们是作为类来实现的,而不是结构体。 NSString, NSArray和 NSDictionary实例总是作为一个已存在实例的引用而不是拷贝来赋值和传递。
类和结构体之间的选择
类和结构体都可以用来定义自定义的数据类型,作为你的程序代码构建块。
总之,结构体实例总是通过值来传递,而类实例总是通过引用来传递。这意味着他们分别适用于不同类型的任务。当你考虑你的工程项目中数据结构和功能的时候,你需要决定把每个数据结构定义成类还是结构体。
按照通用准则,当符合以下一条或多条情形时应考虑创建一个结构体:
结构体的主要目的是为了封装一些相关的简单数据值;
当你在赋予或者传递结构实例时,有理由需要封装的数据值被拷贝而不是引用;
任何存储在结构体中的属性是值类型,也将被拷贝而不是被引用;
结构体不需要从一个已存在类型继承属性或者行为。
合适的结构体候选者包括:
几何形状的大小,可能封装了一个 width属性和 height属性,两者都为 double类型;
一定范围的路径,可能封装了一个 start属性和 length属性,两者为 Int类型;
三维坐标系的一个点,可能封装了 x, y和 z属性,他们都是 double类型。
在其他的情况下,定义一个类,并创建这个类的实例通过引用来管理和传递。事实上,大部分的自定义的数据结构应该是类,而不是结构体。
网友评论