How do you read Objective-C if you only know Swift, and what looks different but means the same?

4.7.1 · L2 · Interview

Predict first

What does this print? (CountOf is Objective-C and calls [items count] on a possibly nil pointer.)

print(CountOf(nil))
  • 0
  • Crash
  • nil

Predict first

In Objective-C, what does if (obj) test?

// A: that the object is valid and non-empty
// B: that the pointer is not nil
// C: that the object responds to isEqual:
  • A
  • B
  • C

Mental model. It is a dialect of the same language, with different punctuation. The grammar is familiar; the pitfalls are in the idioms.

The shapes map one to one. A method call [obj doThing:x with:y] is obj.doThing(x, with: y). @interface declares the public API (in the .h), @implementation holds the code (in the .m). A leading - is an instance method, + a class method. @property declares a property with attributes (strong, weak, copy, nonatomic, readonly) and, by default, a backing instance variable named _name. Objects are created with [[Foo alloc] init], and an initialiser typically reads if ((self = [super init])) { … } return self;. id is “any object”, NSInteger and BOOL (with YES and NO) are plain C values, and literals are @"text", @42, @[…] and @{…}.

The differences that bite: sending a message to nil returns zero instead of failing, so a missing object often produces silent no-ops; == compares pointers, while equality is isEqual:; if (obj) tests for non-nil; any pointer may be nil unless annotated; arrays hold objects only, so numbers are boxed in NSNumber; and a missing return or a stray ; may compile with only a warning. Read the property attributes first, because they tell you who owns what.

Messaging nil returns zero, so the count of “no array” is 0, with no crash.

It is only a pointer check. An empty string or array is still a non-nil pointer, so the test is true.

Common traps

  • Comparing strings with ==. It tests identity, so it works by accident for literals and fails for dynamic strings.
  • Treating nil messaging as a bug. It is defined behaviour, which is why bugs from missing objects hide so well.

They might follow up with

  • What does @synthesize do? It names the backing instance variable; modern compilers do it automatically.
  • Where is the “private” API? In a class extension (@interface Foo ()) at the top of the .m.

Related questions

  • 4.1.1 What happens when you send a message in Objective-C, and what if nothing responds?
  • 4.5.1 How do nullability annotations change what Swift sees, and what happens when they lie?

How do Objective-C blocks differ from Swift closures, especially for… →