Objective-C literals and subscripts

If you’re using clang from their website instead of sticking with Apple’s release, you get support for Objective-C literals and object subscripting. I thought I’d take the BrowseOverflow app and apply this new syntax to it. Notice that the code below doesn’t match what’s in github, which still works with currently-released versions of Xcode and their compilers.

Indexed/Keyed subscripting: we’ve seen this before

Using the syntax described above, you can subscript into an object using something that looks like the traditional C square bracket notation. If you use an integer, you get indexed subscripting:

        Answer *thisAnswer = question.answers[indexPath.row];

If you use an object, you get keyed subscripting:

            NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithCapacity: 1];
            if (localError != nil) {
                userInfo[NSUnderlyingErrorKey] = localError;
            }

This is something that’s been available in many languages before. Smalltalk (from which Objective-C derives) had a well-defined subscripting syntax across all objects, using at: and set:at:. C++ permits classes to supply the operator[]() method to use C-style index subscripting just as Objective-C does.

An aside in defence of operator overloading

If you look at traditional Objective-C syntax, you can see that (roughly speaking, and anyone who points out the edge case isn’t my friend) there are the things in square brackets that are objects and messages, and the things without square brackets are primitive types. There are two different worlds, and never the twain shall meet.

But actually, we’ve learned that encapsulation is good, and that allowing people to concisely express their intent is better than making them deal with our implementation details. Therefore we want to integrate our data types in the language. We want to tell people “add this thing to the other thing”, not “you need to call this function with these parameters which will add the things”.

Providing custom implementations of the standard operators is the best way of doing that. Yes, it hides what’s happening: that’s the point. Yes, it can be abused: the entire software industry is based on a foundation that makes it possible to write bad software. If you want to take away tools that can be used to introduce bugs, you need to take away everyone’s compilers and interpreters.

An aside on the aside about Objective-C operator overloading

So far, Objective-C objects can provide custom implementations of two C operators: the field access operator . used for type safe property access, and the subscript operator [] used as we saw before I digressed.

The reason subscript overloading works in ObjC is that it’s illegal to apply arithmetic operations to object pointers. In C, foo[bar] is just a fancy way of writing *(foo+bar) (which is why bar[foo] also works). If you’re not allowed to apply the [] operator to an id, then you know something else must be happening: i.e. you know that the object subscript behaviour is required.

Well the fact that you can’t do pointer arithmetic on an id means that you could also, for example, check for illegal use of the + operator and call -objectByAddingObject:.

Back to the point: boxing done well.

Many languages make some attempt to “box” primitive types like numbers in high-level value types like number objects. This often causes problems: for example in Java, which has both automatic boxing and method overloading, I could do this:

public void foo(int x) { … }
public void foo(Java.lang.Integer x) {…}

foo(3);

It’s not clear whether 3 refers to the primitive type or to the object type, and therefore it’s not clear which method will get called.

The same problem could have been encountered in ObjC: does foo[3] refer to indexed subscripting or to keyed subscripting using an NSNumber instance?

Thankfully whoever designed the Objective-C boxing behaviour decided it must always be explicit. You can get a number like this:

- (void)browseOverflowViewControllerTests_viewDidAppear: (BOOL)animated {
    NSNumber *parameter = @(animated);
    objc_setAssociatedObject(self, viewDidAppearKey, parameter, OBJC_ASSOCIATION_RETAIN);
}

but otherwise numbers will always be treated as numbers, not as objects.

When it all gets a bit much

You’ve got two seconds, the house is on fire, what does this line do?

    NSString *questionBody = [parsedObject[@"questions"] lastObject][@"body"];

It can be a bit hard to read that, and to pick out which brackets go with messages and which go with subscripts. I can imagine the sort of people who like to issue pronouncements on whether to use the dot operator to access properties will love the new subscripting syntax.

About Graham

I make it faster and easier for you to create high-quality code.
This entry was posted in code-level, OOP. Bookmark the permalink.

2 Responses to Objective-C literals and subscripts

  1. fabrice says:

    I’m using literals since a few months now, I love them. I left subscripting alone for now with deep regrets as it needs ML only methods like objectAtSubscriptingIndex or something like that.

    For the last line, I had wrote it like that instead:

    NSString* body = (parsedObject[@”questions”].lastObject)[@”body”];

  2. Graham says:

    Those missing methods are easy enough to implement for now as categories on NSArray, NSDictionary and their mutable counterparts.

Comments are closed.