玩Swift,来自Java背景,为什么要选择Struct而不是Class?看起来它们是一样的东西,只不过Struct提供的功能更少。那为什么选择它呢?
当前回答
从值类型和引用类型的角度来回答这个问题,从苹果博客的这篇文章来看,它看起来非常简单:
使用值类型[例如struct, enum]: 用==比较实例数据是有意义的 你希望副本有独立的状态 这些数据将在代码中跨多个线程使用 在以下情况下使用引用类型[例如class]: 比较实例标识和===是有意义的 您希望创建共享的、可变的状态
正如在那篇文章中提到的,没有可写属性的类将与结构体的行为相同,但有一点需要注意:结构体最适合线程安全模型——这是现代应用程序架构中日益迫切的需求。
其他回答
从值类型和引用类型的角度来回答这个问题,从苹果博客的这篇文章来看,它看起来非常简单:
使用值类型[例如struct, enum]: 用==比较实例数据是有意义的 你希望副本有独立的状态 这些数据将在代码中跨多个线程使用 在以下情况下使用引用类型[例如class]: 比较实例标识和===是有意义的 您希望创建共享的、可变的状态
正如在那篇文章中提到的,没有可写属性的类将与结构体的行为相同,但有一点需要注意:结构体最适合线程安全模型——这是现代应用程序架构中日益迫切的需求。
一些好处:
由于不可共享,自动线程安全 由于没有isa和refcount,使用更少的内存(实际上通常是堆栈分配) 方法总是静态分派的,所以可以内联(尽管@final可以为类这样做) 更容易推理(不需要“防御性复制”,这是典型的NSArray, NSString等…)出于与线程安全相同的原因
结构vs类
[堆栈vs堆] [值vs参考类型]
结构更可取。但是缺省情况下,Struct并不能解决所有问题。通常你会听说值类型是在堆栈上分配的,但这并不总是正确的。只有局部变量被分配到堆栈上
//simple blocks
struct ValueType {}
class ReferenceType {}
struct StructWithRef {
let ref1 = ReferenceType()
}
class ClassWithRef {
let ref1 = ReferenceType()
}
func foo() {
//simple blocks
let valueType1 = ValueType()
let refType1 = ReferenceType()
//RetainCount
//StructWithRef
let structWithRef1 = StructWithRef()
let structWithRef1Copy = structWithRef1
print("original:", CFGetRetainCount(structWithRef1 as CFTypeRef)) //1
print("ref1:", CFGetRetainCount(structWithRef1.ref1)) //2 (originally 3)
//ClassWithRef
let classWithRef1 = ClassWithRef()
let classWithRef1Copy = classWithRef1
print("original:", CFGetRetainCount(classWithRef1)) //2 (originally 3)
print("ref1:", CFGetRetainCount(classWithRef1.ref1)) //1 (originally 2)
}
*你不应该使用/依赖retainCount,因为它没有提供有用的信息
检查堆栈或堆
在编译过程中SIL(Swift中间语言)可以优化你的代码
swiftc -emit-silgen -<optimization> <file_name>.swift
//e.g.
swiftc -emit-silgen -Onone file.swift
//emit-silgen -> emit-sil(is used in any case)
//-emit-silgen Emit raw SIL file(s)
//-emit-sil Emit canonical SIL file(s)
//optimization: O, Osize, Onone. It is the same as Swift Compiler - Code Generation -> Optimization Level
在那里你可以找到alloc_stack(在堆栈上的分配)和alloc_box(在堆上的分配)
(优化级别(SWIFT_OPTIMIZATION_LEVEL)]
在Swift中,引入了一种新的编程模式,称为面向协议编程。
创建型模式:
在swift中,Struct是一种自动克隆的值类型。因此,我们可以免费获得实现原型模式所需的行为。
而类是引用类型,在赋值过程中不会自动克隆。为了实现原型模式,类必须采用NSCopying协议。
浅拷贝只复制指向那些对象的引用,而深拷贝复制对象的引用。
为每种引用类型实现深度复制已成为一项乏味的任务。如果类包含进一步的引用类型,我们必须为每个引用属性实现原型模式。然后我们需要通过实现NSCopying协议复制整个对象图。
class Contact{
var firstName:String
var lastName:String
var workAddress:Address // Reference type
}
class Address{
var street:String
...
}
通过使用结构体和枚举,我们使我们的代码更简单,因为我们不需要实现复制逻辑。
结构是值类型,类是引用类型
值类型比引用类型快 值类型实例在多线程环境中是安全的 多线程可以改变实例,而不必担心 关于竞争条件或死锁 与引用类型不同,值类型没有引用;因此, 就是没有内存泄漏。
在以下情况使用值类型:
你希望副本有独立的状态,数据才会被使用 跨多线程的代码
在以下情况使用引用类型:
您希望创建共享的、可变的状态。
更多的信息也可以在苹果文档中找到
https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
额外的信息
Swift值类型保存在堆栈中。在进程中,每个线程都有自己的堆栈空间,因此没有其他线程能够直接访问您的值类型。因此,没有竞争条件、锁、死锁或任何相关的线程同步复杂性。
值类型不需要动态内存分配或引用计数,这两者都是昂贵的操作。同时,值类型上的方法是静态分派的。就性能而言,这为值类型创造了巨大的优势。
作为提醒,这里有一个Swift列表
值类型:
结构体 枚举 元组 基本类型(Int, Double, Bool等) 集合(数组,字符串,字典,集合)
引用类型:
类 任何来自NSObject的东西 函数 关闭