玩Swift,来自Java背景,为什么要选择Struct而不是Class?看起来它们是一样的东西,只不过Struct提供的功能更少。那为什么选择它呢?
当前回答
结构和类之间的相似之处。
我用简单的例子来创建主旨。 https://github.com/objc-swift/swift-classes-vs-structures
和差异
1. 继承。
结构不能在swift中继承。如果你愿意
class Vehicle{
}
class Car : Vehicle{
}
参加一个培训班。
2. 经过
Swift结构按值传递,类实例按引用传递。
语境的差异
结构常量和变量
示例(用于2014年全球开发者大会)
struct Point{
var x = 0.0;
var y = 0.0;
}
定义一个名为Point的结构体。
var point = Point(x:0.0,y:2.0)
现在如果我试着改变x,它是一个有效的表达式。
point.x = 5
但如果我定义一个点为常数。
let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.
在这种情况下,整个点是不可变常数。
如果我使用类Point代替,这是一个有效的表达式。因为在一个类中,不可变常量是对类本身的引用,而不是它的实例变量(除非那些变量定义为常量)
其他回答
许多Cocoa api需要NSObject子类,这迫使你使用class。但除此之外,你可以使用以下苹果Swift博客中的案例来决定是使用struct / enum值类型还是类引用类型。
https://developer.apple.com/swift/blog/?id=10
一些好处:
由于不可共享,自动线程安全 由于没有isa和refcount,使用更少的内存(实际上通常是堆栈分配) 方法总是静态分派的,所以可以内联(尽管@final可以为类这样做) 更容易推理(不需要“防御性复制”,这是典型的NSArray, NSString等…)出于与线程安全相同的原因
在这些回答中没有注意到的一点是,持有类和结构的变量可以是let,同时仍然允许对对象的属性进行更改,而对于结构则不能这样做。
如果你不希望变量指向另一个对象,但仍然需要修改对象,即在有许多实例变量的情况下,你希望一个接一个地更新,这是很有用的。如果它是一个结构,你必须允许变量被重置为另一个对象使用var,因为在Swift常量值类型正确地允许零突变,而引用类型(类)不这样做。
我不会说结构体提供的功能更少。
当然,self是不可变的,除了在突变函数中,但仅此而已。
继承可以很好地工作,只要您坚持每个类都应该是抽象的或最终的。
将抽象类实现为协议,将最终类实现为结构。
struct的好处是你可以在不创建共享可变状态的情况下使你的字段可变,因为写时复制会照顾到这一点:)
这就是为什么下面例子中的属性/字段都是可变的,我不会在Java或c#或swift类中这样做。
示例继承结构,在底部名为" Example "的函数中有一点脏和直接的用法:
protocol EventVisitor
{
func visit(event: TimeEvent)
func visit(event: StatusEvent)
}
protocol Event
{
var ts: Int64 { get set }
func accept(visitor: EventVisitor)
}
struct TimeEvent : Event
{
var ts: Int64
var time: Int64
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
}
protocol StatusEventVisitor
{
func visit(event: StatusLostStatusEvent)
func visit(event: StatusChangedStatusEvent)
}
protocol StatusEvent : Event
{
var deviceId: Int64 { get set }
func accept(visitor: StatusEventVisitor)
}
struct StatusLostStatusEvent : StatusEvent
{
var ts: Int64
var deviceId: Int64
var reason: String
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
func accept(visitor: StatusEventVisitor)
{
visitor.visit(self)
}
}
struct StatusChangedStatusEvent : StatusEvent
{
var ts: Int64
var deviceId: Int64
var newStatus: UInt32
var oldStatus: UInt32
func accept(visitor: EventVisitor)
{
visitor.visit(self)
}
func accept(visitor: StatusEventVisitor)
{
visitor.visit(self)
}
}
func readEvent(fd: Int) -> Event
{
return TimeEvent(ts: 123, time: 56789)
}
func example()
{
class Visitor : EventVisitor
{
var status: UInt32 = 3;
func visit(event: TimeEvent)
{
print("A time event: \(event)")
}
func visit(event: StatusEvent)
{
print("A status event: \(event)")
if let change = event as? StatusChangedStatusEvent
{
status = change.newStatus
}
}
}
let visitor = Visitor()
readEvent(1).accept(visitor)
print("status: \(visitor.status)")
}
这个答案最初是关于结构和类之间性能的差异。不幸的是,关于我使用的测量方法有太多的争议。我把它留在下面,但请不要过多地去理解它。我认为经过这么多年,在Swift社区中,struct(以及enum)由于其简单和安全而一直是首选。
如果性能对你的应用很重要,那就自己衡量。我仍然认为大多数时候结构性能更优越,但最好的答案就像有人在评论中说的那样:这要看情况。
===旧答案===
由于结构实例是在堆栈上分配的,而类实例是在堆上分配的,因此结构有时会快得多。
但是,您应该始终自己衡量它,并根据您独特的用例进行决定。
考虑下面的例子,它演示了使用结构和类包装Int数据类型的两种策略。我使用10个重复值是为了更好地反映现实世界,其中有多个字段。
class Int10Class {
let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
init(_ val: Int) {
self.value1 = val
self.value2 = val
self.value3 = val
self.value4 = val
self.value5 = val
self.value6 = val
self.value7 = val
self.value8 = val
self.value9 = val
self.value10 = val
}
}
struct Int10Struct {
let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
init(_ val: Int) {
self.value1 = val
self.value2 = val
self.value3 = val
self.value4 = val
self.value5 = val
self.value6 = val
self.value7 = val
self.value8 = val
self.value9 = val
self.value10 = val
}
}
func + (x: Int10Class, y: Int10Class) -> Int10Class {
return IntClass(x.value + y.value)
}
func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
return IntStruct(x.value + y.value)
}
使用以下方法来衡量性能
// Measure Int10Class
measure("class (10 fields)") {
var x = Int10Class(0)
for _ in 1...10000000 {
x = x + Int10Class(1)
}
}
// Measure Int10Struct
measure("struct (10 fields)") {
var y = Int10Struct(0)
for _ in 1...10000000 {
y = y + Int10Struct(1)
}
}
func measure(name: String, @noescape block: () -> ()) {
let t0 = CACurrentMediaTime()
block()
let dt = CACurrentMediaTime() - t0
print("\(name) -> \(dt)")
}
代码可以在https://github.com/knguyen2708/StructVsClassPerformance上找到
更新(2018年3月27日):
Swift 4.0, Xcode 9.2,在iPhone 6S, iOS 11.2.6上运行Release build, Swift编译器设置为-O -全模块优化:
类版本用时2.06秒 Struct版本耗时4.17e-08秒(快了50,000,000倍)
(我不再平均多次运行,因为方差非常小,低于5%)
注意:在没有整个模块优化的情况下,差异会小很多。如果有人能指出这面旗子到底是干什么的,我会很高兴。
更新(2016年5月7日):
Swift 2.2.1, Xcode 7.3,在iPhone 6S, iOS 9.3.1上运行Release build,平均运行5次,Swift编译器设置为-O -whole-module-optimization:
类版本花费了2.159942142s struct版本耗时5.83 e -08秒(快37,000,000倍)
注意:正如有人提到的,在现实场景中,一个结构中可能会有多个字段,我已经为结构/类添加了10个字段而不是1个字段的测试。令人惊讶的是,结果变化不大。
原始结果(2014年6月1日):
(在struct/class上运行,只有1个字段,而不是10个)
在Swift 1.2、Xcode 6.3.2、iPhone 5S和iOS 8.3上运行Release版本时,平均运行超过5次
类版本花费了9.788332333s Struct版本花费0.010532942秒(快900倍)
旧结果(未知时间)
(在struct/class上运行,只有1个字段,而不是10个)
在我的MacBook Pro上发布:
类版本花费了1.10082秒 struct版本花了0.02324秒(快了50倍)