Containers

Objective-C has the same exact core containers that Swift does, with the two exceptions being that they are named slightly differently, and all of the containers in Objective-C are reference types because of the basic requirement that all Objective-C types must be reference types.

Arrays

In Objective-C arrays are called NSArray. Let's take a look at the initialization of an array in both Swift and Objective-C side-by-side:

var array = [Int]()
NSArray *array = [NSArray alloc];
array = [array init];

We have defined a variable called array that is a reference to the type NSArray. We then assign it to a newly allocated instance of NSArray. The square bracket notation in Objective-C allows us to call methods on a type or on an instance. Each separate call is always contained within a single set of square brackets. In this case, we are first calling the alloc method on the NSArray class. This returns a newly allocated variable that is of the type NSArray.

In contrast to Swift, Objective-C requires a two-step process to initialize a new instance. First, the memory must be allocated and then it must be initialized. Allocating means that we are reserving the memory for that object and initializing it means that we are setting it to its default value. This is what we are doing in the second line. The second line asks the instance to initialize itself. We reassign the array to the result of the call to init, because it is possible for init to return nil. Note that we are not dereferencing the array variable in order to make a call on it. We actually call the methods directly on the pointer.

Now, it is kind of a waste to use two lines to initialize a new instance, so often the calls are chained together:

NSArray *array = [[NSArray alloc] init];

This calls alloc on NSArray and then immediately calls on init on the result of that. The array variable is then assigned to the result of the init call. Be aware that it is possible for alloc to return nil, in which case we would be calling init on nil. In Objective-C this is OK; if you call a method on nil, it will simply always return nil. This is similar to how optional chaining works in Swift.

There is also an alternative to calling alloc and init; it's called simply new:

NSArray *array = [NSArray new];

This class method allocates and initializes the instance at the same time. This is great when you are not passing any arguments into init, but you will still need to call alloc separately when you are passing arguments into it. We will see examples of this later on.

You may have noticed that we have not specified what type this array is supposed to hold. This is because it is actually not possible. All arrays in Objective-C can contain any mix of types as long as they are not C types. This means that an NSArray cannot contain an int (there is an NSNumber class instead), but it can contain any mix of NSStrings, NSArrays, or any other Objective-C type. The compiler will not do any form of type checking for you, which means that we can write code expecting the wrong type to be in the array. This is yet another classification of bug that Swift makes impossible.

So how do we add objects to our array? The reality is that the NSArray class does not allow us to add or remove objects from it. In other words, NSArray is immutable. Instead, there is a version of an array called NSMutableArray that allows us to add and remove objects. Then we can use the addObject: method:

NSMutableArray *array = [NSMutableArray new];
[array addObject:@"Hello World!"];

Methods in Objective-C and Swift are named in the same way with a colon indicating each argument. In Objective-C, the colon is also used when calling the method to indicate the following code is the value to pass into the method.

The existence of a plain NSArray is to serve the same basic purpose as a constant array in Swift. In fact, we will see that all Objective-C containers are split into mutable and non-mutable versions. A mutable container can be passed into a method and treated like the non-mutable version to add some safety by not allowing unwanted code to modify the array.

Now, to access a value in an NSArray we have two options. The full way is to use the objectAtIndex: method:

NSString *myString = [array objectAtIndex:0];

We can also use square brackets, similar to Swift:

NSString *myString = array[0];

Note that we are just assuming that the type returned from the array is an NSString. We can just as easily assume that it is another type, say NSArray:

NSArray *myString = array[0];

As we know, this will be wrong and will almost certainly cause bugs later in the code but the compiler will not complain.

Lastly, to remove an object from a mutable array, we can use the removeObjectAtIndex: method:

[array removeObjectAtIndex:0];

The other important feature that you will need to be aware of is that Objective-C also has array literals, so you don't have to build them up dynamically:

NSArray *array = @[@"one", @"two", @"three"];

Array literals start with an @ symbol just like a string, but then it is defined by a list of objects within square brackets just like Swift.

There is a lot more that arrays can do, but you should be able to understand what each method does when you see it because most are well named. The methods are also often named the same in each language or you can look them up online, where Apple has extensive documentation. The purpose of this chapter is just to get you comfortable enough to have a high-level understanding of Objective-C code.

Dictionaries

Following the same pattern as arrays, dictionaries in Objective-C are called NSDictionary and NSMutableDictionary. A dictionary is initialized in the exact same way as shown:

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSDictionary *dict2 = [NSDictionary new];

To set a value, we use the setObject:forKey: method:

[dict setObject:@"World" forKey:@"Hello"];

Just like with arrays, we cannot set new objects on non-mutable dictionaries. Also, this is our first example of a method that takes more than one argument. As you can see, each argument is contained within the square brackets but separated by a space and the label for that argument. In this pattern, Objective-C methods can have a number of arguments.

Now to access a value we can use the objectForKey: method or square brackets again:

NSString *myString = [dict objectForKey:@"Hello"];
NSString *myString2 = dict[@"Hello"];

Again, we are assuming that the resulting object being returned is a string, because we know what we just put into the dictionary. This assumption isn't always safe and we also need to always be aware that this method will return nil if an object does not exist for that key.

Lastly, to remove an object, we can use the removeObjectForKey: method:

 [dict removeObjectForKey:@"Hello"];

This is all relatively straightforward, especially when you are reading the code. This verbosity was always a great feature of Objective-C to write understandable code and this was definitely carried forward into Swift.

Dictionaries also have literals, but unlike NSArrays and Swift array literals, dictionary literals in Objective-C are declared using curly brackets. Otherwise, it looks very similar to Swift:

NSDictionary *dict3 = @{@1: @"one", @2: @"two", @3: @"three"};

Again, we have to start our literal with an @ symbol. We can also see that we can use numbers as objects in our containers as long as we put an @ symbol before each one. Instead of creating something such as an int type, this creates an NSNumber instance. You shouldn't need to know much about the NSNumber class except that it is a class to represent many different forms of numbers as objects.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset