Siesta

Memory Management

Note that in the first example in the README, no code calls any sort of “removeObserver” method. Siesta can automatically remove observers when they are no longer needed by tying them to the lifecycles of other objects.

Siesta achieves this by introducing a notion of observer ownership, which ties an observer to the lifecycle of some object. Here’s how this mechanism plays out in a few common cases:

Self-Owned Observer

An observer can register as its own owner using the single-argument flavor of addObserver(). This essentially means, “Someone else owns this observer object. Keep only a weak reference to it. Send it notifications until it is deallocated.”

This is the right approach to use with an object implementing ResourceObserver that already has a parent object — for example, a UIView or UIViewController:

class ProfileViewController: UIViewController, ResourceObserver {
        
    override func viewDidLoad() {
        
        someResource.addObserver(self)
    }
}

Observer with an External Owner

An observer can also register with addObserver(observer:, owner:). This means, “This observer’s only purpose is to be an observer. Keep a strong reference and send it notifications until it its owner is deallocated.”

This is the right approach to use with little glue objects that implement ResourceObserver:

someResource.addObserver(MyLittleGlueObject(), owner: self)

This is also the approach you must use when registering structs and closures as observers:

someResource.addObserver(owner: someViewController) {
    resource, event in
    print("Received \(event) for \(resource)")
}

In the code above, the print statement will continue logging until someViewController is deallocated.

Manually Removing Observers

Sometimes you’ll want to remove an observer explicitly, usually because you want to point the same observer at a different resource.

A common idiom is for a view controller to have a settable property for the resource it should show:

var displayedResource: Resource? {
    didSet {
        // This removes both the observers added below,
        // because they are both owned by self.
        oldValue?.removeObservers(ownedBy: self)

        displayedResource?
            .addObserver(self)
            .addObserver(owner: self) { resource, event in  }
            .loadIfNeeded()
    }
}

Detailed Ownership Rules

Observers have owners.

Ownership affects the observer lifecycle.

Observers affect the resource lifecycle.

These rules, while tricky when all spelled out, make the right thing the easy thing most of the time.

Next: Threading