Jul 27, 2018

This post is an enhancement proposal for Objective-C. I’m going to scratch the itch on the surface of possible Objective-C language improvements. The features listed here are chosen to have a relatively small scope in order to be implemented without major changes and in a backwards compatible way in the spirit of Objective-C. Thus they are not meant to turn Objective-C into a modern experimental language like Swift, but should make programming experience better and reduce boilerplate.

Namespaces

Objective-C is infamously verbose. Part of the reason is the lack of namespaces. By convention people use 2-3 letter uppercase prefixes to all their classes to avoid name clashes. So instead of a class called Product you write ACMEProduct . Often you have multiple related class clusters, let’s say we have an ACMEProductTableViewController based on a UITableViewController that displays ACMEProduct items. It will often get a related ACMEProductTableViewCell and an ACMEProductTableDataSource or an ACMEProductTableViewModel . As the app grows it will have “sub-clusters” (-modules/-components) as well, which, for example, group multiple view controllers, so the names become even longer, like ACMEStoreProductTableViewController (assuming that “Store” was our chosen module name).

Most programming languages have a concept of namespaces (or packages) to deal with this problem. The namespaces can be tied to the file system folder structure (like in Java), or purely virtual tree of names (like in C++), but they make the code cleaner. If Objective-C had namespaces, a name like ACMEStoreProductTableViewController could become:

ACME.Store.ProductTable.ViewController

and most of the time the long prefix would be understood from the context, or imported once on the top:

@import_namespace ACME.Store.ProductTable; ... ProductTable.ViewController *vc = ...

And of course, after typing “.” you would get the IDE completion intelligence to help you coding.

enums with strings attached

Objective-C uses the plain C language enumerations. Each enum value is represented by an integer number. This is well until you run the app, then it is not enough. Most of the time it is better to have a human readable string representation for the values. It is a nice feature even if made just for logging and debugging. At other times the enum list is dictated by some API or a protocol, and you have the interchangeable string keys. Sometimes you want to define an NSError which requires not only an integer code from an enum, but also a human-readable error description (potentially localizable). By the way, in many languages (Java, Python, C++, Go etc.) errors require to have such a description and the integer code is optional.

Imagine if this code:

@enum ProductType Free, Demo, Paid, Subscription, @end

would generate a plain C enum for using in switch statements and corresponding string constants for each value that could just be used as needed:

ProductType *type = ProductType.Demo; switch (type.code) { case ProductTypeDemo: ... } NSString *description = type.description; NSLog(@"type = %@", description); // prints: "type = Demo"

public get, private set

If there is a writable property, it implicitly uses a readwrite modifier. If it is not writable, it uses a readonly modifier. There are cases when you want to have a public property such that only the class implementation is able to set. For this case Objective-C has an idiom of declaring a property as readonly in the public interface of the class, and redeclaring it as readwrite in the private interface. This yields this kind of code duplication:

// public interface @property (readonly, ...others...) ACMEProduct *product; // private interface @property (readwrite, ...repeat others...) ACMEProduct *product;

This feels like boilerplate code when adding or renaming properties. In fact this is the major use case for the readwrite modifier, because otherwise it is used by default and can be omitted.

The second declaration could be stripped down if only there was a special modifier for this case, like:

// public interface @property (private_setter) ACMEProduct *product;

In this case the private setter should be only visible and accessible from the @implementation part much like the generated ivar.

Autogenerated DI constructors

In many cases after passing a parameter to the initializer method, this parameter is used to just set a private instance variable or property with the same name:

// interface @property (readonly, nonnull) NSString *title; - (nonnull instancetype)initWithTitle:(nonnull NSString *)title; // implementation - (nonnull instancetype)initWithTitle:(nonnull NSString *)title { ... _title = title; ... }

As you can see in this example the title name repeats 7 times! Adding, renaming or removing title requires 7 modifications. This is a very common conventional code. It is used a lot for dependency injection and just normal initialization. It’s a pity that it requires so much boilerplate.

It’s not trivial to fix this, but much like property setters are generated for us, the DI constructors could be generated too. One possible solution is to generate a constructor method that sets all readonly properties from its parameters. For example, having 2 properties:

@property (readonly, nonnull) NSString *title; @property (readonly, nonnull) UIColor *color;

should automatically generate an initializer with 2 parameters initWithTitle:color: that sets these properties from parameters. In addition this initializer could call a custom initializer method with a fixed name didInit (if it exists in the class) in order to allow extra initalization steps after the properties were set.

Nullability type checks

In 2015 Xcode 6.3 introduced nullability annotations to Objective-C. With those you can express an intent that a pointer to an object can never be nil . Unfortunately the only observable check that the Objective-C compiler does with that is that it prevents you from passing the nil constant in places where a nonnull type is expected. If you pass in a nullable pointer variable (not a nil constant), this is silently allowed:

@property (nullable) NSString *name; - (void)printName:(nonnull NSString *)name; ... [self printName:self.name]; // silently allowed

We know that the nonnull type is more restrictive, but the type system just ignores this restriction, and silently casts it from nullable to nonnull or from unspecified to nonnull . This is similar to “casting away constness” in C/C++, which is not possible to do by accident:

The Objective-C compiler could do the same for nonnull types, and raise a special warning in such cases that would require to do an explicit type cast from nullable to nonnull , or to provide a default nonnull value with the Elvis operator ?: .

Automatic weak self in blocks

Fairly often when you use blocks (closures) you want to call a method of your object like so:

[self.api requestDataWithCompletionBlock:^(id data) { [self displayData:data]; }];

The problem with this code is that it sneaks a retain cycle: the self object keeps and retains the api object, the api object needs to remember the completion block until the request finishes, and the completion block retains self , hence the cycle. If api does not clean up the block, the self object is never released. Even if it does clean up, you lose the ability to destroy self before the request completes.

There’s an Objective-C idiom called “weak-strong dance” which is used to deal with this:

__weak typeof(self) weakSelf = self; [self.api requestDataWithCompletionBlock:^(id data) { __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf) { ... do something with strongSelf that expects a non-nil value } }];

In this case the block does not capture self . Instead it captures the weakSelf variable, which is fine, because it is a “weak” pointer. A weak pointer doesn’t prevent destruction, and it becomes nil after that.

Imagine a piece of code that uses a lot of blocks, including nested blocks where on completion you want to do something else asynchronously, so you have to make a second strongSelf . This pattern becomes boilerplate code! It is possible to shrink the lines by using @weakify/@strongify custom helper macros, but you still have to write them and have them available in your project.

First of all, it’s a good practice to always avoid capturing self . It would be nice to have a warning that tells if self is used anywhere in blocks (excluding “non-escaping” blocks like the one you pass to UIView.animateWithDuration:animations: or NSArray.enumerateObjectsUsingBlock: ).

Secondly, weakSelf could be a magic variable that Objective-C understands. Much like self is a predefined magic variable within any method body, weakSelf could also be predefined, and weakly reference the self object.

Finally, strongSelf , or let it be called “blockSelf”, could be a predefined variable that is unique per block, and strongly captures the known weakSelf , so the boilerplate is reduced to just if check in case you need it:

[self.api requestDataWithCompletionBlock:^(id data) { if (blockSelf) { ... } ];

or just a method call (because calling it on nil is a “noop” which is often fine):

[self.api requestDataWithCompletionBlock:^(id data) { [blockSelf displayData:data]; ];

Final notes

There’s plenty of ways to improve Objective-C. One obvious pie in the sky is supporting custom generic types. It is easy to find many more ideas from other popular languages and imagine how their Objective-C applications would look like. If you find doing this interesting please share your Objective-C enhancement ideas in the comments on Reddit or message me on Twitter.

Cover image by Nick Kenrick under CC BY.