iOS | ARC & Memory Management in

2022-02-09  本文已影响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:

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

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

上一篇下一篇

猜你喜欢

热点阅读