How Does UIView -removeFromSuperview work and What Does It Mean for Objective-C's Inheritence Model?

While working on a project today I encountered an interesting problem: How does UIView implement its -removeFromSuperview method? I'd like to detail the steps I took to figuring out the answer, and also show what I've learned because of it.

The Problem

UIView is the main drawing and container class for iOS. It maintains a tree of subviews, thus creating a hierarchy of rectangles on-screen which we like to call “Views”. Each view on screen has a parent view (called its superview) and 0 or more child views inside it (called subviews) (technically, a subview may appear to be visually outside its container, but it's still considered to be inside). As you can imagine, every screen you see on an iOS device has one big containing root view, which in turn has subviews inside it, each of which have subviews inside of them, and so on. This is the iOS view hierarchy.

UIView has two main methods to manipulate its place in the hierarchy: -addSubview:, which is used by a parent superview (“parent superview” is redundant, I'm aware, but it makes it simpler to explain) to add a child; and -removeFromSuperview, which is called on a child in order to remove it from its superview.

It's very easy to imagine an implementation of -addSubview:. Here's a simplified version of how it might look:


- (void)addSubview:(UIView *)subview {
    [subview removeFromSuperview];
    [subview setSuperview:self];
    [_subviews addObject:subview];
}
It first removes the subview from its existing superview, then sets its superview to be the parent (self), and finally adds it to our mutable array of subviews. Simple enough. But now it comes time to dig in to -removeFromSuperview, and things become a little trickier. At first glance, a very simplified version might look like this:

- (void)removeFromSuperview {
    [self retain];
    // ...
    NSMutableArray *superSubviews = [_superview subviews];
    [superSubviews removeObject:self]; 
    // ... cleanup
    [self release];
}

It seems simple enough, until you look at the documentation for UIView and realize subviews is declared as a readonly property of type NSArray, and NSArray cannot be modified. So while it's possible to access the subviews of our superview, it's returned to us in an immutable array, so we can't directly remove the current view from it.

Investigate By Example

It just so happens UIView on iOS shares the same fundamental design as NSView on Mac OS X (internally, UIView is different because it's using a hierarchy of CALayers, but as far as the application programmer is concerned, the two view classes are strikingly similar). Looking at the documentation for NSView, we quickly find ourselves in with the same problem: we can't modify the array of subviews given to us by -subviews. But NSView is still useful to us, not for its implementation on Mac OS X, but because of its heritage.

It just so happens the NS of NSView stands for NeXTSTEP, an operating system built in the late 1980s, and later acquired by Apple and transformed into Mac OS X. But before that happened, an open source implementation of the NextStep API was started, and exists today as a project called GNUstep. Also, there exists another open source project called Cocotron, which exists as a re-implementation of the Cocoa API on Windows. The benefit of both these projects is it gives some insight on how Cocoa, and thus NSView, could be implemented (that is not to say they show precisely how Apple does it, but it's probably not terribly far off, especially when it concerns just the basic mechanics, which is what we're focusing on).

With these projects in mind, a simple Google search for “NSView.m” turns up some excellent results. If you peer into the implementations of NSView in either project, you can get a feel for how it works. Unfortunately, GNUstep kind of cheats here, as their -subviews method returns an NSMutableArray, unlike Apple's implementations. However, Cocotron's implementation lead me to the correct answer, and a great lesson about how Objective-C's @protected encapsulation works.

(Brief interlude about gawking at Open Source implementations of AppKit or UIKit in order to learn how they work):

In addition to scouring through the GNUstep and Cocotron source code, either of which may be significantly behind Apple's implementations at any given time, you can also look to 280 North's Cappuccino framework. While Cappuccino is written in Objective-J and not Objective-C, the syntaxes are very similar, and the Cappuccino API is modelled after Cocoa. While they don't match up one-to-one, Cappuccino's API are much more modern and in-tune with Apple's newer offerings. It's another great way to figure out how parts of AppKit (or other first-party Frameworks) might be implemented.

If all else fails, it's always worth Googling “XXAppleClass.m” to see if somebody has re-implemented the class themselves.

The Lesson Learned About Objective-C's Encapsulation Model

The solution ended up being rather simple: an object's instance variables are protected by default in Objective-C. This means the class in which they are declared, and any subclasses thereof, may access the variable directly, but no other class is permitted to do so (Objective-C has a little-used “direct-access-variable” operator ->, but it's ugly and generally frowned-upon. I consider it cheating). Accessing the variable directly from within the class is very simple, if you're dealing in terms of your own instance variables. But when your object has a reference to another object of the same class, you still can't access its instance variables without using the arrow operator.

In our case of -removeFromSuperview, both self and superview are instances of UIView, but we can't simply get superview's instance variables without resorting to the arrow.

Instance methods, on the other hand, are inherently public. If Apple had implemented UIView with an instance method to directly access its _subviews mutable array instance variable, then any class could modify the list at will. There needs to be a way for classes of the same type to be able to modify instance variables, but to not allow for other classes to do so. Public methods are not the answer.

The answer are methods declared in a class extension, which is a way to add essentially private methods to a class. Here is my guess for how UIView does it, using my logic:


// UIView.m
@interface UIView ()
- (NSMutableArray *)privateMutableSubviews;
// ...
@end

@implementation UIView
// ...
- (NSArray *)subviews {
    // The public method
    return [NSArray arrayWithArray:_privateSubviews];
}


- (NSMutableArray *)privateMutableSubviews {
    // The private method, only used internally
    return _privateSubviews; // the mutable, internal instance variable
}


- (void)removeFromSuperview {
    [self retain];
    // ...
    [[_superview privateMutableSubviews] removeObject:self];
    // ...
    [self release];
}

// ...
@end

Because both _superview and self share the same UIView lineage, they both have access to the -privateMutableSubviews declared in the class extension, while no outside classes can.

The data is encapsulated and protected, and only the view hierarchy can touch it.

In the end, this ended up solving the wrong problem for me, but I realized it was a good exploration into the Objective-C programming language and I thought it was worth sharing.

Speed of Light