After having seen in the first part of this guide the basic control structures that Objective-C inherits from C, in this second part we will have a look at how we use objects and collections in Objective-C.
Table of contents
Method calling
A note about messaging
Class methods and object creation
Working with nil
Values and literals
Collections
Method calling
Let’s start with method calling, which is what confuses people the most when they approach the language. The reason for this confusion is the syntax Objective-C uses for method calling, which is different from what you usually find in the majority of languages out there.
Let’s first have a look at a method without parameters. The most common way to call a method on an object in other languages, such as Java, Ruby, C#, Javascript, etc. is to use the dot notation:
object.method();
Where object is an object variable and method is the name of the method we are calling. Objective-C wraps a method call inside square brackets instead:
[object method];
To find out what methods a class provides we usually look at its declaration or at the documentation. This method is declared in this way:
- (void)method;
This method does not return any value because it’s return type is void, and does not accept any parameter, since none is declared. Please notice the – sign at the beginning of the line, which is important and we will see why later.
An example of such a method declaration is the following method of NSMutableArray (which we will see in more detail when we will talk about collections).
- (void)removeAllObjects;
Which we call in this way:
[mutableArray removeAllObjects];
Where mutableArray is a variable of type NSMutableArray. Methods can, of course, also return values, in which case the method declaration usually indicates the type of the returned value in place of void. For example, this is the method to get the length of a string:
- (NSUInteger)length;
and it’s called like this:
NSUInteger stringLength = [aString length];
Where Objective-C becomes a bit more confusing is when we add parameters to method calls. In other object oriented languages, parameters of methods or functions are enclosed in parentheses:
object.method(parameter);
Objective-C uses colons instead of parentheses:
[object method:parameter];
The declaration of such a method is the following:
- (return-type)method:(parameter-type)parameter;
For example, an NSString object has a method that returns a new string obtained by appending another string to it:
- (NSString *)stringByAppendingString:(NSString *)aString;
Which would be called in this way:
NSString *concatenatedString = [aString stringByAppendingString:anotherString];
Now the tricky part. How do we call methods with multiple parameters? In other languages this is usually done by adding the second parameter in the parentheses and separating parameters with commas:
object.method(parameter1, parameter2);
This is where Objective-C becomes the most confusing to newcomers. For methods with multiple parameters, the name of the method is split in multiple parts and the parameters are put in between these parts, using colons. So a multiParameter method call looks as follows:
[object methodPart1:parameter1 methodPart2:parameter2];
And this is how the declaration looks like:
-(return-type)methodPart1:(parameter-type1)parameter1 methodPart2:(parameter-type2)parameter2;
Let’s look at an example to make it more clear. NSString has a method to replace a substring with another string. This is its declaration:
- (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement
and this is how you call it:
NSString *newString = [aString stringByReplacingOccurrencesOfString:target withString:replacement];
The reason why methods in Objective-C get split into pieces is because Objective-C values clarity and readability of code a lot. In this way each method incorporates also an explanation of what parameters it takes, so every time you see a method call, you know exactly what the method needs without looking at the documentation. For this reason Objective-C does not have a feature that other object oriented languages have, which is method overloading where the same method can take different types of parameters. The reason is that when a method takes different parameters in Objective-C, it also gets a different name, so method overload is not needed.
There is a dirty trick used by some developers to make method calls look more like what you find in other languages. The compiler allows you to declare a method by omitting all the parts of its name except the first one, as follows:
- (return-type)method:(parameter-type-1)parameter1 :(parameter-type-2)parameter2;
Which yields this method call:
[object method:parameter1 :parameter2];
I am showing you this only to prepare you to what you might find in some code bases that are not well written. I strongly advise you to never use this trick, since you’ll be fighting against the language, which was designed in this way specifically, and the entire developer community that uses the language every day.
Class methods and object creation
We’ve seen how to call methods on objects, but we still didn’t talk about how we create those objects in the first place. This is done through a feature which is not usually present in other programming languages: in Objective-C classes are objects too. This means that you can call methods on classes as you do on objects.
So, in Objective-C there is no new operator to instantiate objects. You create instances by calling method on classes. The typical use of class methods is to declare factory methods. Factory methods are used to create objects as an alternative of allocating objects directly. So, for example, you can create a new string like this:
NSString *myString = [NSString string];
Calling a method on classes, as you can see, works the same way as calling methods on objects. What is slightly different is the declaration of the method:
+ (NSString *)string;
where the only difference is that the line starts with a + instead of a – as we saw for object method declarations.
Factory methods are a way to create objects, but not the only way. The other one is to allocate directly the object by calling the alloc class method. This is a method of the class NSObject, which is the root class from which all the Objective-C classes inherit. This method takes care to allocate the memory to contain the object, and it also cleans the memory so it won’t contain garbage.
After allocating the object you have to initialize it so that the object has all it’s variables and properties set to proper initial values. You usually do that by calling one of the the initializers on the newly created instance. The common idiom to create and initialize an object is the following:
NSString *myString = [[NSString alloc] init];
As you can see in this example, method calls can be nested, as in any other language and this is always done when creating an object. This idiom for creating objects is so common that actually NSObject has a shorthand class method for it:
NSString *myString = [NSString new];
Pay attention, though, that the init method might return a different object than created by the alloc. For this reason it’s best practice to nest the calls or to use the new method instead. So, do not do this:
// Never do this! NSString *myString = [NSString alloc]; [myString init];
Initialization methods often have parameters, like any other method. In this case you have to call alloc and cannot use new:
NSNumber *aNumber = [[NSNumber alloc] initWithFloat:7.0];
A note about messaging
Now that we’ve seen how method calling works in Objective-C, we can have a little look at how this works behind the scenes to grasp a bit more the power of the language.
Method calling in Objective-C is different from other languages, where calls are decided by the compiler when the application binary is built. Objective-C method resolution is completely dynamic and it’s done at runtime. For this reason in Objective-C we speak about sending messages to objects instead of calling methods (or if we use the latter, we are aware of the difference).
When a message is sent to an object, the runtime checks if that object responds to that message, resolves dynamically its address and executes it. The advantage is that we get the capability to check if a feature exists at runtime and we can decide wether to use it or not. Moreover we can even change our classes at runtime, adding or removing methods and so on. These are very powerful and advanced concepts, so it’s not needed to know or use them when learning the language. Just use message passing as method calling in other languages. But it’s good to spend a couple of words about it now, so that you’ll keep in mind in the future.
Working with nil
We have already seen the nil value used to represent empty object variables. There is one special thing about the nil value in Objective-C which is different from many other languages. You can safely call any method on a nil object. The result will just be no operation at all. If you expect a return value from the method, that will be nil for methods that return objects, 0 for methods that return numbers and NO for methods who return booleans.
This is different from other languages where trying to call a method on a null value results in an exception or a crash. This relieves the developer from many checks for nil values and makes the code more cleaner.
There are still some cases where you actually want to check wether an object is nil or not. You can, of course, use the relational operators. This check, though, is commonly done in Objective-C with a much more common idiom where you actually only provide the variable to the if clause or negate it. Thus:
if (someObject != nil) { ... }
becomes
if (someObject) {
...
}
and
if (someObject == nil) { ... }
becomes
if (!someObject) {
...
}
Values and literals
C offers already basic types for values you need in your code, like numbers, characters and booleans. Objective-C adds some value objects on top of that.
We have already seen one type of value object, NSString, which is used for strings manipulation instead of using the standard arrays of characters offered by C, which are more complicated to use. We already saw how we can allocate a NSString using either one of the factory methods or the direct allocation with initializers. Objective-C offers a third way to create new values: literals.
Literals in Objective-C are obtained by prepending the @ symbol to a C value and are used to create value objects and collections in a more concise way than the ones we already saw. String literals use double quotes, so to create a new string, we can write:
NSString *myString = @"This is a string";
Unlike their counterparts in other languages, many objects in Objective-C are immutable, which means that they cannot be changed once you create them. This is a powerful concept which avoids many bugs introduced by code that changes the content of shared objects. Having immutable objects ensures that you can safely pass them around as parameters or return values without worrying for their content to change. It’s a concept that is advisable to introduce in your classes as well, when possible. NSString is one of the classes of which the instances are immutable and the class offers many methods to derive new strings without changing the content of the original ones. If you really need to change the content of a string for some reason, there is the NSMutableString subclass of NSString that actually allows this.
Objective-C offers also a class for booleans and other basic numeric values: NSNumber. This is needed for collections, for example, which can contain only objects and not basic types. NSNumber has a list of factory methods and initializers to create instances from any type of C number. Moreover, Objective-C literals also work with NSNumber, thus making the creation of numbers much more concise and readable. These are examples on how you do it:
NSNumber *integerNumber = @42; NSNumber *unsignedNumber = @42u; NSNumber *longNumber = @42L; NSNumber *floatNumber = @3.14f; NSNumber *doubleNumber = @3.1415926535; NSNumber *charNumber = @'M'; NSNumber *boolNumber = @YES;
NSNumber instances are immutable too and there is no mutable subclass. Just create new objects when needed.
Literals work also on boxed expressions, so you can create an NSNumber from the result of any expression. Boxed expressions are enclosed in parentheses:
NSNumber *expressionNumber = @(3 * 7);
While some other languages offer unboxing of values, in Objective-C there is none. So, if you need to calculate expressions between NSNumbers, you have to retrieve their basic valuse first using the appropriate methods.
Collections
The C language offers arrays to hold collections of scalar values and objects, but those are usually cumbersome and complicated to use, especially if you want dynamic collections that change in size. For this reason C arrays are almost never used in Objective-C, which offers instead more advanced types of collections.
Arrays
The first collection we visit is NSArray, which is used for ordered collections of objects. The literal for creating arrays uses square brackets:
NSArray *someArray = @[firstObject, secondObject, thirdObject];
Before the introduction of literals, arrays were commonly created using factory or init methods and that is still possible. Literals, though, are a more concise and preferred way. This also prevents some errors: if you use the NSArray creation methods that take a variable number of arguments, you have to terminate that list with a nil value. The problem is that it’s possible to truncate the list of items unintentionally if one of the provided values is accidentally nil. The new literal syntax does not have this problem.
The objects contained in an array don’t have to be all of the same type. Accessing array objects at specific indexes is done through subscripting, like this:
id firstArrayItem = someArray[0];
Pay attention that if you request an invalid index out of the array boundaries you will get an exception at runtime which will crash your app.
Collections are another set of objects in Objective-C that are immutable. If you need to perform an operation that actually requires the collection content to change, this is sometimes done through methods that actually return new copies of the collection instead of modifying the original one.
For example, if we want to add an object at the end, the arrayByAddingObject: method returns a new array with the new item appended at the end:
NSArray *newArray = [someArray arrayByAddingObject:newObject];
There are cases where you actually need to change the content of a collection. In this case you can create an instance of it’s mutable subclass. You cannot create an instance of a mutable class using literals though, so you have to do it using the other object creation methods. But you can still use subscripting to change the content:
NSMutableArray *mutableArray = [NSMutableArray arrayWithObjects:firstObject, secondObject, thirdObject, nil]; mutableArray[0] = newFirstObject;
Be aware that you cannot extend the array using subscripting. An invalid index will still throw an exception. If you need to make the array bigger use the addObject: method.
You can also get a mutable array from an immutable one with the mutableCopy method:
NSMutableArray *mutableArray = [anArray mutableCopy];
And you can also switch back to an immutable instance when you need to pass a copy around:
NSArray *immutableArray = [NSArray arrayWithArray:mutableArray];
Dictionaries
Another very common collection class is NSDictionary. Dictionaries store key-value pairs and are usually called hashmaps in other languages. It’s best practice to use a string as a key in a dictionary, but it can actually be any object that supports the NSCopying protocol (we will see what protocols are later).
Dictionaries also have factory methods to create them, but again, the preferred way is by using literals. Dictionary literals use braces:
NSDictionary *dictionary = @{@"anObject" : someObject, @"aString" : @"This is a string", @"aNumber" : @7, };
You can query dictionaries using subscripting too, passing a key as a subscript:
NSNumber *storedNumber = dictionary[@"aNumber"];
NSDictionary also has a mutable subclass called NSMutableDictionary. Unlike NSMutableArray, you can use subscripting not only to replace items but also to add new objects to the mutable dictionary. Similarly to arrays, you can switch back and forth between immutable and mutable dictionaries using the mutableCopy and dictionaryWithDictionary: methods.
Sets
A less used but still useful collection is NSSet. Sets are unordered collections of objects. Their main advantage is that they provide performance improvements over arrays when testing for membership. So if you need to know if an object is in a collection, NSSet might be a good choice. Sets have no literal for creation, so they need to be created with factory methods or initializers:
NSSet *aSet = [NSSet setWithObjects:@"This is a string in a set", @7, anObject, nil];
As for other collections, the objects contained in a set don’t have to be of the same type. NSSet instances are also immutable and there is a mutable subclass called NSMutableSet. Objects in sets can be present only once. If you need to put an object in a set more than once, there is the NSCountedSet subclass of NSMutableSet.
Nil values in collections
It’s not possible to insert nil values inside of collections. If you try to do that, you’ll get an exception which will, usually, crash your app (more on exceptions later). So you have to check wether an object is nil before trying to insert it into a collection.
There are cases, though, where you actually might want to do this on purpose. For example, you might want to do it so signal that a position in an array is empty. Since you cannot insert a nil in a collection, Objective-C offers the NSNull class:
NSArray *array = @[anObject, @7, [NSNull null]];
Beware that NSNull does not behave like nil, since it’s actually a real object. If you try to call a method that does not exist on a NSNull object, you will get an exception, so you have to check before calling methods on objects coming from collections that contain NSNull values. NSNull is a singleton class, which means that its null method will always return the same instance. This means that you can check whether an object in a collection is equal to the shared NSNull instance using the equality operators:
id objectFromArray = [anArray objectAtIndex:0]; if (objectFromArray != [NSNull null]) { ... }
Enumerating colletions
When you need to go through all the elements of a collection, Objective-C offers many different ways to do so. C loops work fine of course, but they are prone to programming errors, so it’s advised not to use them. The preferred method to go through a collection is using fast enumeration which has this form:
for (Type variable in collection) { ... }
So, to enumerate all the object in an array we would write:
for (id object in array) { ... }
Fast enumeration works with dictionaries too. In this case the loop iterates through the keys and you have to retrieve the values yourself:
for (NSString *key in dictionary) { id object = dictionary[key]; ... }
You can use break and continue in fast enumeration loops as you would use them in a normal loop. If you need to go through a collection in reverse order, you can use an NSEnumerator object:
for (id object in [array reverseObjectEnumerator]) { ... }
Beware that you cannot change the content of a mutable collection (adding, replacing or removing objects) while you are enumerating it, or you will get an exception. A quick fix to this is to enumerate through a copy of it:
for (id object in [aMutableArray copy]) { ... }
Matteo has been developing apps for iOS since 2008. He has been teaching iOS development best practices to hundreds of students since 2015 and he is the developer of Vulcan, a macOS app to generate SwiftUI code. Before that he was a freelance iOS developer for small and big clients, including TomTom, Squla, Siilo, and Layar. Matteo got a master’s degree in computer science and computational logic at the University of Turin. In his spare time he dances and teaches tango.