美文网首页
iOS | ARC & Memory Management in

iOS | ARC & Memory Management in

作者: 清無 | 来源:发表于2022-02-09 16:27 被阅读0次

    ARC

    ARC works automatically, so you don’t need to participate in reference counting, but you do need to consider relationships between objects to avoid memory leaks. This is an important requirement that is often overlooked by new developers.

    An Object’s Lifetime

    The lifetime of a Swift object consists of five stages:

    • Allocation: Takes memory from a stack or heap.
    • Initialization: init code runs.
    • Usage.
    • Deinitialization: deinit code runs.
    • Deallocation: Returns memory to a stack or heap.

    Reference counts, also known as usage counts, determine when an object is no longer needed. This count indicates how many “things” reference the object. The object is no longer needed when its usage count reaches zero and no clients of the object remain. The object then deinitializes and deallocates.

    Weak References

    To break strong reference cycles, you can specify the relationship between reference counted objects as weak.

    Unless otherwise specified, all references are strong and impact reference counts. Weak references, however, don’t increase the reference count of an object.

    In other words, weak references don’t participate in the lifecycle management of an object. Additionally, weak references are always declared as optional types. This means when the reference count goes to zero, the reference can automatically be set to nil.

    weak var owner: User?
    

    This breaks the User to Phone reference cycle by making the owner reference weak.

    Unowned References

    Strong Weak Unowned
    unowned let user: User
    

    user is now unowned, breaking the reference cycle and allowing every object to deallocate. Build and run to confirm.

    Reference Cycles With Closures

    For example, if you assign a closure to a property of a class, and that closure uses instance properties of that same class, you have a reference cycle. In other words, the object holds a reference to the closure via a stored property. The closure holds a reference to the object via the captured value of self.

    Capture Lists

    var x = 5
    var y = 5
    
    let someClosure = { [x] in
      print("\(x), \(y)")
    }
    x = 6
    y = 6
    
    someClosure()        // Prints 5, 6
    print("\(x), \(y)")  // Prints 6, 6
    
    • x is in the closure capture list, so you copy x at the definition point of the closure. It’s captured by value.

    • y is not in the capture list, and is instead captured by reference. This means that y will be whatever it is when the closure runs, rather than what it was at the point of capture.

    Capture lists come in handy for defining a weak or unowned relationship between objects used in a closure.

    lazy var completePhoneNumber: () -> String = { [unowned self] in
      return self.countryCode + " " + self.number
    }
    

    This adds [unowned self] to the capture list for the closure. It means that you’ve captured self as an unowned reference instead of a strong reference.

    The syntax used here is actually a shorthand for a longer capture syntax, which introduces a new identifier. Consider the longer form:

    var closure = { [unowned newID = self] in
      // Use unowned newID here...
    }
    

    Here, newID is an unowned copy of self. Outside the closure’s scope, self keeps its original meaning. In the short form, which you used above, you are creating a new self variable, which shadows the existing self variable only during the closure’s scope.

    Cycles With Value Types and Reference Types

    Swift types are reference types, like classes, or value types, like structures or enumerations. You copy a value type when you pass it, whereas reference types share a single copy of the information they reference.

    struct Node { // Error
      var payload = 0
      var next: Node?
    }
    

    Hmm, the compiler’s not happy. A struct value type cannot be recursive or use an instance of itself. Otherwise, a struct of this type would have an infinite size.

    class Node {
      var payload = 0
      var next: Node?
    }
    
    do {
      let ernie = Person(name: "Ernie")
      let bert = Person(name: "Bert")
      
      ernie.friends.append(bert) // Not deallocated
      bert.friends.append(ernie) // Not deallocated
    }
    

    ernie and bert stay alive by keeping a reference to each other in their friends array, although the array itself is a value type.

    Make the friends array unowned and Xcode will show an error: unowned only applies to class types.

    // 1
    class Unowned<T: AnyObject> {
      unowned var value: T
      init (_ value: T) {
        self.value = value
      }
    }
    
    // 2
    var friends: [Unowned<Person>] = []
    
    // 3
    do {
      let ernie = Person(name: "Ernie")
      let bert = Person(name: "Bert")
      
      ernie.friends.append(Unowned(bert))
      bert.friends.append(Unowned(ernie))
    }
    

    To access the Person object within Unowned, use the value property, like so:
    let firstFriend = bert.friends.first?.value // get ernie

    相关文章

      网友评论

          本文标题:iOS | ARC & Memory Management in

          本文链接:https://www.haomeiwen.com/subject/hgzbkrtx.html