Memory management for Swift value types and reference types

Source: Internet
Author: User
Tags hashable

1. Memory allocation 1.1 value type memory allocation
  • Value types that are fixed long in Swift are stored on the stack and do not involve memory on the heap. Variable-length value types (strings, type of values with variable lengths) allocate heap memory.

    • This is equivalent to a "benefit", which means that you can use a value type to complete the execution of a method more quickly.
    • An instance of a value type saves only its internal storage properties, and the instances that are assigned by "=" are stored separately from each other.
    • The assignment of a value type is a copy, and for a fixed-length value type, the cost of such a copy is completed in constant time because the required memory space is stationary.
    struct Point {    var x: Double    var y: Double}
    let point1 = Point(x: 3, y: 5)var point2 = point1print(point1)           // Point(x: 3.0, y: 5.0)print(point2)           // Point(x: 3.0, y: 5.0)
  • The above example is actually allocated on the stack as.

               栈point1   x: 3.0         y: 5.0point2   x: 3.0         y: 5.0
  • If you try to modify point2 a property, only the value saved in the address on the stack will be modified point2 x , and the value will not be affected point1 .

    point2.x = 5print(point1)           // Point(x: 3.0, y: 5.0)print(point2)           // Point(x: 5.0, y: 5.0)
               栈point1   x: 3.0         y: 5.0point2   x: 5.0         y: 5.0
1.2 Memory allocations for reference types
  • The storage properties of the reference type are not stored directly on the stack, and the system creates a space on the stack to hold the instance pointers, and the pointers on the stack are responsible for finding the appropriate objects on the heap.

    • The assignment of a reference type does not occur "copy", and when you try to modify the value of the example, the pointer of the instance "directs" you to the heap and then modifies the contents on the heap.
  • Pointthe definitions below are modified into classes.

    class Point {    var x: Double    var y: Double    init(x: Double, y: Double) {        self.x = x        self.y = y     }}
    let point1 = Point(x: 3, y: 5)let point2 = point1print(point1.x, point1.y)           // 3.0  5.0print(point2.x, point2.y)           // 3.0  5.0
  • Because Point it is a class, so Point the storage properties can not be stored directly on the stack, the system will open up on the stack of two pointers to save point1 the length point2 of the pointer, the stack of pointers to the heap to find the corresponding object, point1 and point2 two instances of the storage properties will be stored on the heap.

  • When you use "=" To assign a value, a pointer is generated on the stack point2 , pointing to the point2 point1 same address of the heap as the pointer.

               栈              堆point1   [    ] --|                  |-->  类型信息point2   [    ] --|     引用计数                        x: 3                        y: 5
  • After the pointer is generated on the stack, the contents of the point1 point2 pointer are empty, then the memory is allocated to the heap, the heap is locked, the appropriate memory space is found, the target memory is allocated and the heap is unlocked, and the first address of the memory fragment in the heap is stored in a pointer on the stack.

  • Compared to saving on the stack point1 and point2 , the heap needs more memory space, in addition to save x and y space, in the head also requires two 8-byte space, a pointer to the type information to index the class address, a "reference count" to save the object.

  • When you try to modify point2 the value, point2 the pointer "directs" you to the heap, and then modifies the contents of the heap, which point1 is also modified.

    point2.x = 5print(point1.x, point1.y)           // 5.0  5.0print(point2.x, point2.y)           // 5.0  5.0
  • We call point1 point2 this relationship "shared" with each other. "Sharing" is an attribute of a reference type and, in many cases, a problem, the root cause of the "shared" pattern is that we cannot guarantee the immutability of an object of a reference type.

2, variability and immutability
    • The variability and immutability of objects in Swift are limited by keywords let var .

    • The default state of the Swift language is immutable and is manifested in many places.

      • For example, the method will be copied when the argument is passed in, and the copied parameter is immutable.
      • Or when you use var a keyword to define an object that does not change, the compiler will remind you to change it var to let .
2.1 Variability and immutability of reference types
  • For objects of reference types, when you need an immutable object, you cannot control the immutability of its properties by means of a keyword.

  • When you create an instance of the point class, you want it to be immutable, so use the let keyword declaration, but let can only constrain the content on the stack, that is, Even if you use the let keyword for a type instance, you can only guarantee that its pointer address does not change, but that its properties cannot be constrained.

      class Point {var x:double var y:double init (x:double, y:double) {self.x = x self.y = y}}  
      let point1 = Point (X:3, y:5) Let Point2 = Point (x:0,                     y:0) print (point1.x, POINT1.Y)//3.0 5.0print (point2.x, POINT2.Y)//0.0 0.0point1 = Point2 A compile error occurred and the Point1 pointer could not be modified point1.x = 0//Because the X attribute is defined using VAR, it can be modified by print (point1.x, poi NT1.Y)//0.0 5.0print (point2.x, POINT2.Y)//0.0 0.0  
  • If you set all the properties to be immutable, it does guarantee that the reference type is immutable, and many languages are designed to do so.

    class Point {    let x: Double    let y: Double    init(x: Double, y: Double) {        self.x = x        self.y = y     }}
    let point1 = Point(x: 3, y: 5)print(point1.x, point1.y)           // 3.0  5.0point1.x = 0                        // 发生编译错误,x 属性是不可变的
  • The new problem is that if you want to modify Point the properties, you can only re-build an object and assign a value, which means that there is no necessary locking, addressing and memory recycling process, greatly wasting the performance of the system.

    let point1 = Point(x: 3, y: 5)point1 = Point(x: 0, y: 5)
2.2 Variability and immutability of value types
    • Because properties of value types are stored on the stack, they can be constrained by the let keyword.

    • You can declare a property of a value type var , guaranteeing its flexibility, when you want an instance of that type to be an immutable object, use a let declarative object, even if the object's properties are mutable, but the object as a whole is immutable, so you cannot modify the properties of the instance.

      struct Point {    var x: Double    var y: Double}
      let point1 = Point(x: 3, y: 5)print(point1.x, point1.y)           // 3.0  5.0point1.x = 0                        // 编辑报错,因为 point1 是不可变的
    • Because the assignment is "copy", the variability limit of the old object does not affect the new object.

      let point1 = Point(x: 3, y: 5)var point2 = point1                 // 赋值时发生拷贝print(point1.x, point1.y)           // 3.0  5.0print(point2.x, point2.y)           // 3.0  5.0point2.x = 0                        // 编译通过,因为 point2 是可变的print(point1.x, point1.y)           // 0.0  5.0print(point2.x, point2.y)           // 0.0  5.0
3. Sharing of reference types
  • "Sharing" is an attribute of a reference type and, in many cases, a problem, the root cause of the "shared" pattern is that we cannot guarantee the immutability of an object of a reference type.

  • The following shows the shares in the app type.

    // 标签class Tag {    var price: Double    init(price: Double) {        self.price = price    }}// 商品class Merchandise {    var tag: Tag    var description: String    init(tag: Tag, description: String) {        self.tag = tag        self.description = description    }}
    let tag = Tag(price: 8.0)let tomato = Merchandise(tag: tag, description: "tomato")print("tomato: \(tomato.tag.price)")          // tomato: 8.0// 修改标签tag.price = 3.0// 新商品let potato = Merchandise(tag: tag, description: "potato")print("tomato: \(tomato.tag.price)")          // tomato: 3.0print("potato: \(potato.tag.price)")          // potato: 3.0
  • The scenario described in this example is "sharing", where you modify the part you want (the price of potatoes), but it causes unexpected changes (the price of tomatoes) because tomatoes and potatoes share a label instance.

  • Semantic sharing is caused by memory addresses in a real-world memory environment. The objects in the example above are reference types, and since we have created only three objects, the system allocates three memory addresses on the heap, respectively, to save tomato , potato and tag .

                  栈                堆tamoto   Tag         --|         description   |       tag                       |--> price: 3.0                       |patoto   Tag         --|         description
  • In the OC era, there are no such rich value types to use, many types are reference types, so using reference types requires a security policy that does not generate "sharing," which is one of them.

  • First create a Label object, put the price you need on the label, then call the method on the label and copy() pass the returned copy object to the product.

    let tag = Tag(price: 8.0)let tomato = Merchandise(tag: tag.copy(), description: "tomato")print("tomato: \(tomato.tag.price)")          // tomato: 8.0
  • When you pass the execution to the tag copy Merchandise constructor, the memory allocation is as follows.

                  栈                 堆tamoto   Tag         -----> Copied tag         description        price: 8.0                               tag                            price: 8.0
  • If there is a new product on the shelves, you can continue to use the "copy" to label.

    let tag = Tag(price: 8.0)let tomato = Merchandise(tag: tag.copy(), description: "tomato")print("tomato: \(tomato.tag.price)")          // tomato: 8.0// 修改标签tag.price = 3.0// 新商品let potato = Merchandise(tag: tag.copy(), description: "potato")print("tomato: \(tomato.tag.price)")          // tomato: 8.0print("potato: \(potato.tag.price)")          // potato: 3.0
  • Now in-memory allocations.

                  栈                 堆tamoto   Tag         -----> Copied tag         description        price: 8.0                               tag                            price: 3.0patoto   Tag         -----> Copied tag         description        price: 3.0
  • This copy is called a "protective copy", and in the protected copy mode, no "sharing" is generated.

4. Copy of variable-length value type
    • Variable-length value types do not keep everything on the stack like fixed-length value types, because the memory space on the stack is contiguous, and you always open and release the stack's memory by moving the tail pointer. In Swift, collection types and string types are value types, and identity information for variable-length value types is preserved on the stack, while internal elements of variable-length value types remain on the heap.

    • The fixed-length value type does not occur "shared," which is well understood, since each assignment will open up new stack memory, but for a variable-length value type, how does it handle the heap memory used to keep the inner elements? In WWWDC2015 's No. 414 video, Apple revealed a copy of the fixed-length value type: The copy rules for variable-length value types are more complex than "copy" and reference-type "protected copies" of fixed-length value types, using a technique called copy-on-write, The literal understanding is that it is only copied at the time of writing.

    • There are a number of swift native variable-length value types in Swift 3.0, which use copy-on-write technology to improve performance when copying, such as Date, Data, Measurement, URL, Urlsession, Urlcomponents, Indexpath.

5. Sharing with reference types
  • Sharing is not always harmful, and one of the benefits of sharing is that the memory space on the heap is reused, especially for objects with large memory footprint (compared to slices). So if the object on the heap is not modified in the "shared" state, then we should reuse the object to avoid creating duplicate objects on the heap, what you need to do is create an object and then pass the object's pointer to the object's referrer, simply by using "share" to implement a "cache" policy.

  • If you use a lot of repetitive content in your application, such as using many similar images, if you call the method in every place you want, you UIImage(named:) create a lot of duplicate content, so we need to create all the images we use, and then select the desired images from them. Obviously, in this scenario, the dictionary is best used as a container for cached images, and the dictionary key values are used as image index information. This is one of the classic use cases of reference types, and the dictionary's key values are "identity information" for each image, and you can see how important "identity information" is in this example.

    enum Color: String {    case red    case blue    case green}enum Shape: String {    case circle    case square    case triangle}
    let imageArray = ["redsquare": UIImage(named: "redsquare"), ...]func searchImage(color: Color, shape: Shape) -> UIImage {    let key = color.rawValue + shape.rawValue    return imageArray[key]!!}
  • A variable-length value type actually saves the memory on the heap, so creating a variable-length value type will inevitably lock up the heap and allocate memory, and one of the purposes of using the cache is to avoid excessive heap memory operations, which in the previous example we habitually put String As the key value for the dictionary, but String is a variable-length value type that triggers a memory allocation on the heap when the key is generated in searchimage .

  • If you want to continue searchImage the performance of the promotion, you can use the fixed-length value type as the key value so that the memory on the heap is not accessed when the key value is synthesized. One thing to note is that the fixed-length value type you are using must satisfy the Hashable protocol to be the key value for the dictionary.

    enum Color: Equatable {    case red    case blue    case green}enum Shape: Equatable {    case circle    case square    case triangle}struct PrivateKey: Hashable {    var color: Color = .red    var shape: Shape = .circle    internal var hsahValue: Int {        return color.hashValue + shape.hashValue    }}
    let imageArray = [PrivateKey(color: .red, shape: .square): UIImage(named: "redsquare"),                  PrivateKey(color: .blue, shape: .circle): UIImage(named: "bluecircle")]func searchImage(privateKey: PrivateKey) -> UIImage {    return imageArray[privateKey]!!}

Memory management for Swift value types and reference types

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.