iosxcodeuiviewuikitsuperview

Is UIView superview property weak or strong?


The UIView header states that superview property is strong

open var superview: UIView? { get }

But it behaves just like a weak property, i.e. if I create view1 and view2 and then call view1.addSubview(view2), and then save a strong ref to view2 only (not to view1), view1 will be deinited even though view2 references it through its superview property.

So, I wonder how it is implemented in reality.

Edit: for example, this code prints "deinit" (the ViewController instance is shown on the screen), which means that view1 is deinited, even though view2 should hold it strongly through the superview property.

class View: UIView {
    deinit {
        print("deinit")
    }
}

class ViewController: UIViewController {

    var view2 = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()

        let view1 = View()
        view1.addSubview(view2)
    }
}

Solution

  • Oleg's answer is correct, but it's worth digging into this further. You're looking at this interface definition:

    open var superview: UIView? { get }
    

    and you're assuming that means this is a strong property. It doesn't say that at all. It says UIView has a readonly superview property. There is no setter, so you would not expect any memory management annotations (strong/weak/unowned).

    Even if it had a setter, for example, UIView.backgroundColor:

    var backgroundColor: UIColor? { get set }
    

    This tells us exactly nothing about the memory management. There is no promise that this UIColor will be retained by the view. It is free to make its own copy. It is free extract information out of the UIColor and generate some other object for its internal use (like a CGColor), and throw this away. There is no promise that backgroundColor has a backing ivar. This is free to be a computed setter.

    Some properties, like delegates, are marked weak:

    weak var transitioningDelegate: UIViewControllerTransitioningDelegate? { get set }
    

    You generally can trust that these do not retain the object passed, but remember that these are informational, not guarantees. Consider this perfectly legal (and completely horrible) Swift:

    class AnotherClass {
        deinit { print("deinit") }
    }
    
    class MyClass {
        private var _myProp: AnotherClass?
        weak var myProp: AnotherClass? {
            get { return _myProp }
            set { _myProp = newValue }
        }
    }
    

    myProp claims to be weak but actually does retain its value. You should never do this, but the point is that the language doesn't stop you.

    The take-away from all this is that if you care that an object continues to exist, it is your responsibility to maintain a strong reference to it. When you don't care if it exists, you should release your strong reference to it. You should avoid relying on some other object to maintain it for you.

    (In practice, there are many real cases where it is incredibly handy to rely on the fact that some other object will hold something for you. For example, we of course rely heavily on the fact that Arrays hold strong references to their contents. But it's up to you to be certain that the container is promising that behavior if you need it. Looking at the interface is not sufficient.)

    To the specific question of UIView, this is a computed property. While an implementation detail, here is roughly how it's implemented in iOS 10.1:

    - (UIView *)superview {
        UIView *result;
        if ([UIView _isAccessingModel] != 0x0) {
                id visualState = [self visualState];
                result = [visualState mSuperview];
        }
        else {
                if ([self viewFlags] & 0x400000)) {
                        CALayer *superLayer = CALayerGetSuperlayer([self layer]);
                        result = nil;
                        if (superlayer != nil) {
                                result = CALayerGetDelegate(layer);
                        }
                }
        }
        return result;
    }