When, as a developer, you come from another language to Objective-C, you usually want to map the general concepts about programming you already know to this new language that at first might seems obscure. I remember myself being confused by the Objective-C syntax when I started learning it. What at first look might not make sense actually does a lot when you get a grasp of it and in my opinion (and the opinion of many other developers) makes the code much more readable. What at first sight might seem complicated is just something a bit different from what you are used to, thus feeling unfamiliar.
This guide is intended for developers who already know object oriented programming and goes over all the concepts you need to understand in order to use the language and learn programming for iOS and OS X.
It does not assume, though, that you know already C as other guides do. Many times, when people approach the language the first time, they get said to study C first. Although Objective-C is an object oriented extension built on top of C and inherits its the syntax, primitive types, and flow control statements, there are things you probably won’t ever use while writing Objective-C code or do it in very rare and specific occasions. So I believe that Objective-C and the parts you need from C can be learned along to each other. This guide integrates all the parts of C you need to know to start using the language, while ignoring the rest.
Table of contents
Comments
Variables and basic types
Operators
Object variables
A note on prefixes and namespaces
Branching and decisions
Relational and logical operators
Loops
Comments
Comments in Objective-C come directly from C and are similar to what you find in other languages. One line comments are written like this:
// This is a single line comment.
Everything after the // is ignored by the compiler until the end of the line. They also work after instructions:
int counter = 0; // This is a counter.
C allows also multiline comments, which some languages don’t have
/*
This is a multiline comment
which spans more than one line.
*/
Variables and basic types
As in many other languages, you need to store your values and object inside variables. In some languages you can just use variables freely when you need them. Moreover you can change the content of the variable to anything you want: an integer, a decimal number, a string or an arbitrary object.
This does not happen in C. Variables are typed, which means that you have to declare the type of data a variable will contain and you won’t be able to change it later. Variable declarations in C look like this:
int variable1 = 7; float variable2 = 3.14; char variable3 = 'm';
Variable names can contain digits but cannot start with them and cannot include spaces, but can include capital letters (used for camel case) or the underscore character _
As you might have noticed in the example above, each instruction in C ends with a semicolon, which is required. Many languages do to, but some don’t, so this might be new for you. You will learn fast to end instructions with semicolons.
Take the habit of always initializing your variables to a specific value, even 0. In some languages variables are always initialized to 0 if no other value is specified. This does not happen in an Objective-C method, where non initialized variables will contain garbage and cause strange behaviours in your programs.
These are the most common types of C used in Objective-C programs.
Type Description Examples int integer numbers, including negatives 0, 105, -12 unsigned int positive integers 0, 27, 315 float, double floating point decimal numbers 1.61, -17.375 bool boolean values true, false char single text character 'a', 'F', '?'
If you need bigger numbers that the int type can contain, there are the long and the long long types, with the related unsigned types.
Now, the fact is that when you look at Objective-C code, you rarely find these basic types, which some developers and Apple SDKs still use, while you find others more. Here are some common ones you will find often:
NSInteger, for integer numbers NSUInteger, for positive integers CGFloat, for floating point decimal numbers, used for graphical values like drawing coordinates or sizes of graphical objects BOOL, for booleans, which uses YES and NO instead of true and false
The reason these types where created was to make transition from 32 bits to 64 bits architectures easier. C types map to a specific sizes in memory, while the types above change regarding to the architecture the code is compiled for. Objective-C also has its own BOOL type (the reason for it is a bit more complicated).
Try to use the latter types when you can and pay attention to which types are used and returend by APIs, or you might end with weird results if values are truncated when switching between the two types.
C allows also to define other kind of simple types, a practice that is used extensively in Objective-C. We will see these types later.
Operators
Like any other language, C has its own arithmetic operators to work with the numeric values stored in the variables (which are probably the same as the ones you already know).
The arithmetic operators of C are the following:
Operator Description Syntax + addition a + b - subtraction a - b * multiplication a * b / division a / b % module (integer remainder of division) a % b
Arithmetic operators follow standard math operator precedence, so multiplication, division and modulo have precedence over addition and subtraction.
C has also increment and decrement operators:
Operator Description Syntax ++ increment a++ or ++a -- decrement a-- or --a
Increment and decrement operators have precedence over the arithmetic operators. Pay particular attention to these operators. When used in isolation, they are equivalent:
someVariable++;
produces the same result as
++someVariable;
The difference comes out when you use them inside another expression. When put before the variable, they increment or decrement the value before it is used. When put after, they increments or decrement the value after using it.
This example explains how this works:
int x; int y; // Increment operators x = 1; y = ++x; // x is now 2, y is also 2 y = x++; // x is now 3, y is 2 // Decrement operators x = 3; y = x--; // x is now 2, y is 3 y = --x; // x is now 1, y is also 1
We have already seen the assignment operator = which might be different in the languages you are used to. That is not the only assignment operator. The arithmetic operators (with the exception of the modulo) attach to the assignment and produce another four assignment operators, which are used as a shorthand for incrementing, decrementing, multiplying and dividing the content of a variable:
Operator Example Shorthand for += a += 10; a = a + 10; -= a -= 7; a = a - 7; *= a *= 3; a = a * 3; /= a /= 5; a = a / 5;
Unlike in other languages, in Objective-C there is no operator overloading. This means that these operators (and the other operators we will see later) only work with basic types and they do not work with objects (to concatenate strings, for example).
Object variables
Let’s have a first look at objects. Objective-C is an object oriented language after all, so we need variables to contain objects. The generic type for an object variable is id:
id myObject = nil;
The id type means that the variable can contain any type of object. The nil value is equivalent to what is called null in some other languages (and in C) and means the variable is empty and does not contain any object. Objective-C is a dynamically typed language, so we can declare variables of type id and use them for any object. We can then call any method we want on that object, regardless of the fact that that method might exist or not. This might be useful in some cases, but the majority of the time we want the compiler to check for our mistakes as soon as possible and give variables a defined type.
Object variables with a specific type are declared in a slightly different way:
NSString *aString = nil;
Now what does that * mean? Well, strictly speaking that means that this variable is a pointer, a thing that makes many programmers recoil in horror. Pointers are one of those things coming from C you don’t want to deal with. Many modern languages don’t have pointers, so this might be a concept you are not familiar with. Although in the future, when you will be more familiar with the language, it might be useful to know what a pointer is, this is something you don’t have to worry about for now. You can write Objective-C without knowing this concept yet.
When programming I rarely think in terms of pointers. Just remember this simple rule: you need a * when you declare an object variable and you don’t when you use it.
A note on prefixes and namespaces
As you probably noticed, all types in Objective-C have some prefix at the beginning of the name (NSString, CGFloat, etc). The reason is that Objective-C does not have namespaces. To avoid name collisions (different objects ending up having the same name) Apple decided to prepend a two letters prefix to its types and classes. The two letter are generally coming from the framework declaring the type: UIKit classes and types have a UI prefix (UIView, UILabel, etc.), Core Graphics uses the CG prefix (CGFloat, CGRect, etc.) and the Foundation and AppKit frameworks (the latter present only on the Mac) use NS (NSString, NSArray, etc.). Why NS? Because these libraries were first developed at NeXT for the NeXTSTEP operating system.
It is a good practice to you add a prefix to your classes and types too. Even if you are not developing a framework but a normal app (which is often the case), just use letters from the app name to be sure to avoid collisions to code you might add later from a different source (like some open source code you might use in your project). There is no strict check from the compiler, so you can omit it, but it’s better not to.
Branching and decisions
Let’s now see how flow control works in Objective-C. Flow control is another part that the language inherits directly from C, so the structures are the same. Decisions and branching happen, in Objective-C, through three branching statements.
If-else statement
The main statement to perform decisions in Objective-C is the if-else statement
if (expression) { if-body } else { else-body }
The if-body is executed only if the expression to YES, true or any non zero value. Otherwise the else-body is executed. The else clause can be omitted like in many other languages. In the case of just one instruction in the if-body or else-body, the respective curly braces can be omitted too. If you come from a language that has no curly braces, there are many different styles that state where to place them.
I personally use the K&R style now, that in my experience seems to be quite widespread in the Objective-C community, but I’ve moved through many different styles during my programming career. The only solid advice I can give you regarding this topic is to be consistent: pick one style and always use it everywhere.
If you want to use more than one condition, the if-else statement allows multiple branches. This is usually the case in many languages, although it’s not possible in a few.
if (expression1) { if-body } else if (expression2) { else-if-body } else { else-body }
The else if clause can be repeated as many times as needed.
Switch statement
When you have multiple choices you can use, as we saw, the if-else statement with as many branches as needed. If the expression evaluates to an integer, though, C offers another branching facility, the switch statement
switch (expression) { case value1: case-body-1 break; case value2: case-body-2 break; ... default: default-body break; }
The values for the different cases must be constant integers, which means that you can either write a number or a constant in them, but not a variable.
The break statements are actually used to exit immediately from the switch statement. They are not mandatory, but pay attention not to forget them, otherwise the execution will continue into the next case.
The default statement is optional, and is executed when the value of the expression does not match any case. The break in the default statement is not mandatory either and not necessary unless you put another case after the default, which I strongly advise against, since it would be a poor coding practice.
Ternary operator
Another branching facility that Objective-C inherits from C is the ternary operator, also known as conditional operator. This is an operator that in some languages might not be available, and it’s a shorthand of the if-else statement and a return statement (which we will see later, but you are probably already familiar with). The ternary operator takes this form:
condition ? expression1 : expression2;
If the condition evaluates to YES, true, or any non zero value, expression1 is executed and it’s value is returned, otherwise the same happens for expression2. Thus, it is equivalent to the following:
if (condition) return expression1; else return expression2;
The ternary operator has the advantage over the if-else statement that it can be written inside another expression, like an assignement. Thus this is possible and sometimes used:
a = condition ? expression1 : expression2;
Relational and logical operators
To write the conditions for the branching statement and the loops we will see in a short while, C and Objective-C offer the following relational operators:
Operator Description Syntax == equal to a == b != not equal to a != b > greater than a > b >= greater than or equal to a >= b < less than a < b <= less than or equal to a <= b
Pay attention to the == operator. A very common mistake, especially when coming from other languages where this would not be allowed, is to use = in comparisons instead, which is the assignment operator. This is perfectly legal in C, as any assignment also returns a value that is checked by the if-else statement, which checks for 0 and non zero values. So, the program will compile and run perfectly, but probably not behave as you expect. This is a mistake that costs many people a lot of time in debugging. Luckily the latest versions of XCode might warn you of this common mistake when you build your project, if the proper settings are enabled.
To combine conditions and form compound ones, C offers the common logical operators you find in many other languages:
Operator Description Syntax ! negation (not) !a && and a && b || or a || b
Pay attention to the double symbols used in and and or: C has also the single character versions, which are bitwise operators and not logical ones. We will have a look at these later.
Many languages have a strict boolean notion of true and false. As we have seen, C, and Objective-C, have a more loose notion where zero and non zero values are checked instead. Some old versions of C don’t even have the bool type. It’s certainly beneficial to think in terms of truthiness of the conditions when writing them. It’s also useful nonetheless to know a bit of what happens behind the curtains. Objective-C NO and YES values are also mapped respectively to 0 and 1.
This is useful to know for a very common idiom in Objective-C that comes from C. The nil value is also mapped to 0, which means that it is equivalent to NO. So, instead of checking if a variable (or value) is equal to nil, it’s common in Objective-C to check it’s negation, which is logically equivalent. So instead of writing
if (someObject == nil)
we write
if (!someObject)
Many times, though, checking for nil values is not necessary as it is in other languages. We will see later why.
Loops
Objective-C loops also come from C. In addition to these Objective-C also introduces a statement to enumerate collections that we will see later when we will talk about those. Looping over collections is much more common in an Objective-C program, but the basic loops are still useful nonetheless.
For loop
The first loop statement, present in many modern languages as well is the for loop. In C it has the following syntax:
for (initialization-expression; loop-condition; update-expression) {
loop-body
}
Please notice that the separators between initialization-expression, loop-condition and update-expression are semicolons and not commas.
The for loop is quite flexible and can accept different kind of expressions in it. In it’s most common form, it’s used like this:
The initialization-expression is an expression executed before entering the loop. It’s most commonly used to declare and/or initialize an integer variable used to count through the loop.
The loop-condition is a boolean condition used to check when to terminate the loop. It is evaluated before each pass through the loop. When the loop condition is false, the loop is terminated. This is usually used to check if the counter declared in the initialization-expression is over or under a certain value.
The update-expression is an expression that is evaluated at the end of each pass through the loop, after the loop body has been executed, and just before looping back to evaluate the condition-expression again. It’s usually used to update the value of the counter variable declared in the initialization-expression.
In it’s most common incarnation, the for loop takes the following form:
for (NSUInteger index = 0; index < someValue; index++) { loop-body }
Where someValue is a variable or a constant with some positive integer value. In this way the loop iterates exactly someValue times.
While loop
The for-loop is normally used to iterate a fixed number of times. If we want to loop until a certain condition is met, then the while loop is normally used.
while (condition) {
loop-body
}
The condition is checked at the beginning of every iteration and the loop ends when the condition evaluates to a false value. If it evaluates to false on the first iteration (before executing the loop-body once), then the loop-body is not executed.
Do-while loop
If we want the loop to iterate at least one time, we have the do-while loop.
do { loop-body } while (condition);
In this case the order of loop-body execution and condition evaluation is reversed. First the loop goes through one iteration and then checks the condition. If it evaluates to true, the loop continues, until it evaluates to false.
As for the branching statement, the curly braces can be omitted in any loop if the loop body only has one instruction.
Break and continue
There are two statements that are used to alter the flow of the loop structures: break and continue. They can be used in any loop.
The break statement is used when you want to terminate the loop prematurely, for reasons often different from the loop condition and at any point during an iteration, so you only execute part of it. This illustrates how usually it’s used:
while (loop-condition) { some-instructions if (exit-condition) break; some-other-instructions }
If the exit-condition evaluates to true during any iteration, the loop exits without executing some-other-instructions, and execution continues after the loop. You can of course use more than one break checks in the loop body.
Sometimes you want to just skip the rest of the current iteration like with the break statement, but without exiting the loop. The continue statement is used for this specific reason.
while (loop-condition) { some-instructions if (skip-condition) continue; some-other-instructions }
When the skip-condition is true in any iteration, some-other-instructions are not executed, but the loop continues to the next iteration, checking again the loop-condition and executing the loop-body again.
The break and continue statements can also be mixed inside the same loop, to have a more complex control over the flow structure.
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.
Nice tutorial, Its easy to understand.
Thanks team!
Nice blog Matteo :)
Very clear!!!