Search code examples
memoryswift2rx-swiftretain-cycleretaincount

Using 'self' on RxSwift closures... What about instance methods as param?


In other stack overflow questions, it was emphasized that the capture [weak self] should be used for closures that aren't owned by the class because self could be nil before the closure completes. An alternative when the closure is owned by the class itself is [unowned self].

My question is do I need to use [unowned self] when the function I pass as a parameter is an instance method of the current class?

Example

import RxSwift

class Person {
    var name = "Default name"

    class func getPersons() -> Observable<Person> {
        // ...
    }


}

class MyController: UIViewController {
    let disposeBag = DisposeBag()

    // I know this right
    func unownedDisplayPeople() {

        Person.getPersons()
            .subscribeNext { [unowned self ] person in
                self.displayName(person)
            }
            .addDisposableToBag(disposeBag)
    }

    // But what about this?
    func whatAboutThisDisplayPeople() {

        Person.getPersons()
            .subscribeNext(displayName)
            .addDisposableToBag(disposeBag)
    }

    // Or this?
    func orThisDisplayPeople() {

        Person.getPersons()
            .subscribeNext(self.displayName)
            .addDisposableToBag(disposeBag)
    }

    func displayName(person: Person) {
        print("Person name is \(person.name)")
    }
}

If I still need to think about the reference counting when I just pass an instance method, how do I do it? Where do i put the [unowned self]? Or is it considered [unowned self] already when I just pass the instance method?


Solution

  • Unfortunately, passing an instance method to subscribeNext will retain self. To be more generic, storing a reference to an instance method will increase the retain count of the instance.

    let instance = ReferenceType()
    print(CFGetRetainCount(instance)) // 1
    
    let methodReference = instance.method
    print(CFGetRetainCount(instance)) // 2
    

    The only solution here is do what you have done in unownedDisplayPeople.

    let instance = ReferenceType()
    print(CFGetRetainCount(instance)) // 1
    
    let methodReference = { [unowned instance] in instance.method() }
    print(CFGetRetainCount(instance)) // 1