Is UIView superview property weak or strong?

1.6k views Asked by At

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)
    }
}
3

There are 3 answers

2
Rob Napier On BEST ANSWER

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;
}
0
matt On

The UIView header states that superview property is strong

It doesn't state that at all. What it states is that is property is read-only. You cannot set the superview, so no issue of retention policy even arises. You don't even know whether there is a "superview reference". All you know is that you can ask for the superview by means of property syntax and you will be told what it is. How that is done behind the scenes is not specified and you shouldn't care about it.

Having said that: Obviously a view has no strong reference to its superview, because if it did, there would be a retain cycle (because a superview owns its subview and thus has a strong reference to it).

0
Oleg Sherman On

The superview property is not necessarily a strong property and can be a computed property of other private weak property.

The addSubview method establishes a strong reference from the superview to the subview and not necessarily from the subview to the superview.

By the end of the viewDidLoad method view1 is not in the view hirarchy and does not have any other objects pointing to view1 so it gets deallocated but the view2 has also the view controller pointing to it so view2 is not deallocated.