Search code examples
swiftautomatic-ref-countingdeinit

Swift : Some classes not de-initialised


In the following code NSString and NSNumber are not de-initialised when the reference is removed . NSMutableString and NSAttributedString are de-initialised . What is the criteria for deinit ?

class WeakHolder<R : AnyObject> {
    weak var cheez : R?
    init(_ _cheez : R) {
        cheez = _cheez
    }
}

do {
        var nsStringCollection = [NSString(string: "77"),NSString(string: "99")]
        let weakNSStringHolder = WeakHolder(nsStringCollection[1])
        nsStringCollection.removeLast()
        print("NSString : \(weakNSStringHolder.cheez)")
    }

    do {
        var nsMutableStringCollection = [NSMutableString(string: "77_m"),NSMutableString(string: "99_m")]
        let weakNSMutableStringHolder = WeakHolder(nsMutableStringCollection[1])
        nsMutableStringCollection.removeLast()
        print("NSMutableString : \(weakNSMutableStringHolder.cheez)")
    }

    do {
        var nsNumberCollection = [NSNumber(integerLiteral: 77),NSNumber(integerLiteral: 99)]
        let weakNumberHolder = WeakHolder(nsNumberCollection[1])
        nsNumberCollection.removeLast()
        print("Number : \(weakNumberHolder.cheez)")
    }

    do {
        var nsAttributedCollection = [NSAttributedString(string: "77_atts"),NSAttributedString(string: "99_atts")]
        let weakAttributedHolder = WeakHolder(nsAttributedCollection[1])
        nsAttributedCollection.removeLast()
        print("AttrString : \(weakAttributedHolder.cheez)")
    }

Output :

NSString : Optional(99)
NSMutableString : nil
Number : Optional(99)
AttrString : nil

Solution

  • Short NSString objects are stored directly in their (tagged) pointer, and do not require memory management. Other static strings are stored in the binary and may never be deallocated. Neither allocate memory, so neither must release it.

    NSMutableString and NSAttributedString allocate actual objects, so they also need to deallocate them.

    Both behaviors are implementation details and you should not rely on them. They are not promised.

    The rule for memory management is to hold a strong reference to anything you care about, and remove your strong reference when you no longer care about it. deinit should only cleanup memory (calling free on malloc-blocks, for example, if needed). No "business logic" should be in a deinit; there's no promise it will ever run. (For example, during normal program termination, deinit is skipped, unlike in C++.)