Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

值类型(struct) VS. 引用类型(class) #3

Open
ShannonChenCHN opened this issue Mar 5, 2020 · 2 comments
Open

值类型(struct) VS. 引用类型(class) #3

ShannonChenCHN opened this issue Mar 5, 2020 · 2 comments

Comments

@ShannonChenCHN
Copy link
Owner

No description provided.

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Mar 5, 2020

Swift 中的值类型(Building Better Apps with Value Types in Swift)

2019.09.13

  • Reference semantics
  • Immutability
  • Value semantics
  • Value types in practice
  • Mixing value types and reference types

1. Reference Semantics

  • 使用引用类型时存在的问题
  • 如何解决?
    • copy
    • Foundation 框架中的 NSDictionary 等类都用了 defensive copy 来保证数据安全性

2. Immutablity 是否是一个解决方案?

  • 函数式编程

    • 使用了具有不可变性(Immutablity)的引用类型
    • 消除了很多具有可变性的引用类型带来的问题
  • 函数式编程的缺陷

    • Can lead to awkward interfaces(不可变性的世界过于理想化,无论是编程语言本身,还是状态管理的编程思想,都离不开 mutating)
    • Does not map efficiently to the machine model(与机器代码的映射关系比较难处理)
  • 举例子:埃拉托斯特尼筛选法

    • mutating 版本 VS non-mutating 版本
  • Cocoa(Touch) 中的 Immutablity

    • Cocoa(Touch) 中的 immutable class
      • NSDate, NSURL...
      • 提高了安全性,不再需要 copy 了
    • 缺点:更低效的算法

3. Value Semantics

  • Variables Are Logically Distinct

  • Value Types Compose

    • All of Swift’s “fundamental” types are value types
      • Int, Double, String, ...
    • All of Swift’s collections are value types
      • Array, Set, Dictionary, ...
    • Swift tuples, structs, and enums that contain value types are value types
  • Value Types Are Distinguished by Value

  • Equatable

    • Value types should implement Equatable
  • Mutation When You Want It, But not when you don’t

  • Freedom from Race Conditions

  • Performance: What about all those copies?

    • Copies Are Cheap: Constant time
      • Copying a low-level, fundamental type is constant time
        • Int, Double, etc
      • Copying a struct, enum, or tuple of value types is constant time
        • CGPoint, etc.
      • Extensible data structures use copy-on-write ???
        • Copying involves a fixed number of reference-counting operations
        • String, Array, Set, Dictionary, etc.

4. Value Types in Practice

see sample code

5. Mixing value types and reference types

5.1 Reference Types Often Contain Value Types

Value types generally used for “primitive” data of objects:

class Button : Control {var label: Stringvar enabled: Bool

  // ...

}

5.2 A Value Type Can Contain a Reference

Copies of the value type will share the reference:

struct ButtonWrapper {var button: Button}

Maintaining value semantics requires special considerations:

  • How do we cope with mutation of the referenced object?
  • How does the reference identity affect equality?

5.3 Immutable References and Equatable

Reference identity is not enough:

struct Image : Drawable {
  var topLeft: CGPoint
  var image: UIImage
}

extension Image : Equatable { 
  static func ==(lhs: Image, rhs: Image) -> Bool {
    // return lhs.topLeft == rhs.topLeft && lhs.image === rhs.image
    return lhs.topLeft == rhs.topLeft && lhs.image.isEqual(rhs.image)
  }
}


var image = Image(topLeft: CGPoint(x: 0, y: 0),
                  image: UIImage(imageNamed:"San Francisco”)!)
var image2 = image

在上面的例子中,image 属性是不可变的引用类型,所以不用担心数据的安全性。但是判断相等性时,不能以引用值是否相等作为依据,而应该将其当做值类型一样根据内容去判断对等性。

5.4 References to Mutable Objects

Unexpected mutation:

struct BezierPath: Drawable {
  var path = UIBezierPath()
  var isEmpty: Bool {
    return path.empty
  }func addLineToPoint(point: CGPoint) {
    path.addLineToPoint(point) // Unexpected mutation❌ 
  } 
}

在上面的例子中,因为 path 属性是可变的引用类型,所以在 addLineToPoint 方法被调用时,path 属性值可能会被改变。

5.5 Copy On Write

  • Unrestricted mutation of referenced objects breaks value semantics
  • Separate non-mutating operations from mutating ones
    • Non-mutating operations are always safe
    • Mutating operations must first copy
struct BezierPath: Drawable {private var _path = UIBezierPath()



  var pathForReading: UIBezierPath {return _path
 
  }



  var pathForWriting: UIBezierPath {mutating get {

      _path = _path.copy() as! UIBezierPathreturn _path

    }}}

extension BezierPath {var isEmpty: Bool {
    return pathForReading.empty 
  }mutating func addLineToPoint(point: CGPoint) {
    pathForWriting.addLineToPoint(point)
  }
}

var path = BezierPath()
var path2 = path
var path2 = path
if path.empty { print("Path is empty") }
var path2 = path
path.addLineToPoint(CGPoint(x: 10, y: 20))
path.addLineToPoint(CGPoint(x: 100, y: 125))
  • Uniquely Referenced Swift Objects

The standard library value types uses this throughout:

struct MyWrapper {
  var _object: SomeSwiftObject
  var objectForWriting: SomeSwiftObject {
    mutating get {
     if !isKnownUniquelyReferenced(&_object)) {
        _object = _object.copy()
     }
     return _object
    }
} }

延伸阅读:

小结

  • Maintaining value semantics requires special considerations
  • Copy-on-write enables efficient value semantics when wrapping Swift reference types

参考

@ShannonChenCHN
Copy link
Owner Author

ShannonChenCHN commented Jun 19, 2021

Copy on Write

  • Copy-on-Write 是一种用来优化占用内存大的值类型的拷贝操作的机制。
  • 对于 Int、String 等基本类型的值类型,它们在赋值的时候就会发生拷贝,它们没有 Copy-on-Write 这一特性(因为Copy-on-Write带来的开销往往比直接复制的开销要大)。
  • 对于 Array、Dictionary、Se t类型,当它们赋值的时候不会发生拷贝,只有在修改的之后才会发生拷贝,即 Copy-on-Write。
  • 对于自定义的结构体,不支持 Copy-on-Write。

参考:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant