Categories & Extensions

Categories provide a useful way to split a single class into multiple files. If we have, let’s say, a House class, it may have some methods like “buildWithMaterials” and “returnAddress” which returns the home address.

There may be methods that involve home maintenance such as “needsPlumbingRepair” or “paintWalls”. Instead of throwing these in the general class definition of House, we can include them in a category called “House+Maintenance”. This is a separate file that imports the House class header. At runtime, the methods included in this category will become part of the House class.

Say we create a subclass of House, and that subclassed object will ultimately require maintenance; we can then import the header file of our category so that we have access to the API. This declutters the original House class interface and implementation files, and reduces the number of methods in one place.

Another way we can utilize categories is to use them to hide certain methods. While we can’t truly make a method protected (due to Objective-C’s dynamic runtime), we can simulate one using a category. If we use our House example, let’s say we want a protected method called “marketValue” - we can create a new category for House called House+Protected. In the interface file, we list our marketValue method so that it is publicly available to classes that import this category. In our implementation file, we write the code for our marketValue method, returning some NSNumber.

In some other class where we may create an instance of House, we would be able to send messages to that House object and call a method that is included in the House class interface file. Remember, we should NOT have to import the House+Protected interface file to access those methods; that is the whole point of protecting those methods. If we don’t import the file, we can’t call the marketValue method that we want to; if we try to send the marketValue message to our House object, the compiler will complain.

In order to access the marketValue method, we need to include access to it in a subclass of House. If we create a subclass of House and call it Condo, we inherit all of the properties and methods that House contains, and we can add additional methods here. To hide our marketValue method, we only import House+Protected.h into the implementation file - here we can create a method (lets call it assessValue) and in defining assessValue, we can call our protected method (marketValue). We include assessValue in our Condo interface file, so that it is publicly available - behind the scenes, that method is calling our protected method.

If we import Condo into our main.m file, for example, and try to call out protected method on an instance of Condo *london = [[Condo alloc] init] by declaring [london marketValue], we will get a compiler error. Instead, we should call the Condo method that itself calls our protected method by writing [london assessValue].

One way to completely bypass all of this work is to simply use the performSelector method - since all methods are actually public in Objective-C, there are only ways to simulate privatizing them, although you cannot make them truly protected.

We could just say:
SEL protectedMethod = @selector(marketValue);
if ([london respondsToSelector:protectedMethod]) {
    [london performSelector:protectedMethod];
}

Because our london object is an instance of Condo, and our Condo implementation file imports our protected method, the london object DOES actually respond to the marketValue method. As such, the above code will successfully compile.

Along with categories, we also have extensions. They serve a similar purpose to categories in that they allow you to add methods to a class outside of that class’s main files, the difference being that you don’t use a separate interface and implementation file to define those methods. Instead, you must implement the extension’s API in the main class’s .m file (not in the interface, or .h, file).

Much like categories, we can use extensions to declare private methods. In an implementation file of House, there is a section that says @interface House () and below there is the main implementation part of the implementation file which reads @implementation House.

We declare extensions in the same place we would declare an instance variable - between the @interface House () and @end tags. There, we could have:
-(NSInteger) marketValue {
    return [self currentListedPrice];
}

Since the method isn’t declared in the header, it’s not actually visible to other classes. We would need to call it from another method that is publicly visible (declared in the .h file).