Understanding the Difference Between Functions and Methods in Objective-C

Jamal O'Garro
12 min readAug 8, 2015

A look at functions and methods in the Objective-C programming language.

Understanding the Difference Between Functions and Methods in Objective-C

In the following article we will talk about functions and methods and how we can use them to accomplish our goals as programmers. We are using C and Objective-C to demonstrate these concepts; however, the concepts covered here can be applied to many other programming languages. This article is written to be beginner friendly; however, experienced developers that are new to C and Objective-C should be able to follow along as well.

Functions

Functions are key component of many programming languages. In the simplest sense functions are a way that we can reuse code through our application making it easy for use to execute the same functionality without having to type it out again or copy and paste it from somewhere else in our code. This fits nicely into a principle often referred to as DRY or “don’t repeat yourself.” A good rule of thumb is that if you see your self rewriting or copying and pasting the same piece of code over and over again you should probably wrap it in a function so you can easily reuse it. Since Objective-C is a superset of C it can still rely on it for certain components, including functions.

There are a few things that make a function: its return value (its output if any), its name (how we call it in our program), the arguments that it takes (represented by its parameters) and its executable code, which we normally call a block (the code found between the curly braces). An example of a function can be found below.

// main.m
#import <Foundation/Foundation.h>
// declare our function
int addNumbers(int number1, int number2) {
return number1 + number2;
}

int main(int argc, const char * argv[]) {
@autoreleasepool {

// Call our function and save its return value in a variable
int sum = addNumbers(1, 2);
NSLog(@"The sum is %i", sum);
}
return 0;
}

In the example above we declared and defined a function called addNumbers that takes two numbers, adds them and returns the sum of the two numbers. One thing to note is that when a function returns a value we can actually capture it and store it in a variable. The benefit of having a function that will add numbers is useful because anywhere that we want to add two numbers we can. Well, this may not be the most useful function since we have the “+” operator but you get the idea . . .

The example above displayed how we can create functions that accept arguments and return values that are C primitives; however, we can also pass and return pointers to Objective-C objects to and from functions as well. This is demonstrated in the example below:

// main.m
#import <Foundation/Foundation.h>
// Declare our function
void greetPerson(NSString *name) {
NSString *bigGreeting = [name uppercaseString];

NSLog(@"Hey there, %@%", bigGreeting);
}
// Use the funciton in our main function
int main(int argc, const char * argv[]) {
@autoreleasepool {
greetPerson(@"Jamal"); // "Hey there, JAMAL" will be printed to the console
}
return 0;
}

Our greetPerson function accepts an NSString representing someone’s name (in this came my name) as an argument, and prints the result to the console. We set the return type as void because we don’t actually return anything from this function (more on this later) One note is that functions don’t have to always accept and return the same type. For example, we can have a function that accepts an Objective-C object or a C primitive and returns a boolean value, as demonstrated in the next example:

// main.m
#import <Foundation/Foundation.h>

BOOL compareNames(NSString *firstName, NSString *lastName) {
if (firstName == lastName) {
return YES;
} else {
return NO;
}
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL namesMatch = compareNames("John", "John");
NSLog(@"%hhd", namesMatch); // 1 will logged to the console }
return 0;
}

If look at our two examples above we may notice that we actually wrote our function outside of the main function before actually using it inside of it. This is called declaring the function and is how we let the compiler know about our function before we use it. A function declaration lets the compiler know what the functions return type, name and arguments are so when we call it it knows exactly what it should do. One cool thing to note is that you can declare the function before main but actually implement it later. As long at the compiler knows about the return type, name of the function and its parameters (think of these as temporary variables we use to represent the arguments we are going to pass to the function when calling it) we can call it inside of our main function and implement it (define it’s functionality) later. Here’s an example that demonstrates this:

// main.m
#import <Foundation/Foundation.h>
// Declare our function's return type, name and inputs so the compiler knows about it
BOOL compareNames(NSString *firstName, NSString *lastName);
// Our main function that executes our code
int main(int argc, const char * argv[]) {
@autoreleasepool {
BOOL namesMatch = compareNames("John", "John");
NSLog(@"%hhd", namesMatch); // 1 will logged to the console }
return 0;
}
// We actually define our function and its implementation
BOOL compareNames(NSString *firstName, NSString *lastName) {
if (firstName == lastName) {
return YES;
} else {
return NO;
}
}

Organizing our code in this way helps us make it more readable since the function declarations, implementations and our program logic are all separated into distinct parts of the program.

Functions That Return Nothing

Functions don’t always have to return a value meaning that they can execute some arbitrary block of code without returning anything to the user. Let’s refactor our greetPerson method and have it print a different message to the console depending on the input.

// main.m
#import <Foundation/Foundation.h>

void compareNamesRefactored(NSString *firstName, NSString *lastName) {
if (firstName == lastName) {
NSLog(@"Whoa, this person's first name matches their last name!");
} else {
NSLog(@"This person has a normal name.");
}
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
compareNamesRefactored(@"John", @"David"); // "This person has a normal name." will be logged to the console
}
return 0;
}

By using the “void” keyword we indicate that this function does not return a value. However, it still receives arguments and will print a message to the console depending on the value of those inputs. If your function doesn’t return a value then the return value should be set to “void” to indicate this.

Methods

Methods are pretty much a lot like functions but the only difference is that they are attached to classes and are defined using a slightly different syntax in Objective-C. You can think of a method as a type of function that gives a class a particular functionality. For example, if I have a Person class that goal of the class is to represent a person in my program. The person can have a height, weight, age and profession. These would be considered properties of the class. However, a person can also walk, talk and play video games. These actions that the person can do in our program would be represented in the form of methods.

The syntax for creating methods are a little different (detailed a little further down) than functions but they pretty much have all the same components. A method needs to have a return type, a name and sometimes arguments, which are optional. Since methods add functionality to a class we must also denote whether or not a method is a class method (sometimes referred to as static method) or an instance method. We’ll talk more about the differences below.

Instance Methods vs. Class Methods

Instance methods can be thought of as actions that can be taken on instances of a class. Going back to our Person class example, if we allocate memory for an initialize a person object we can then send “walk” message to that person instance to get the person to walk in our program. The “walk” message is being sent to an instance initiated from the class and not the class itself so it is an instance method.

// Sending the objectAtIndex message to an instance of the NSArray classNSArray *myItems = @[@"Macbook", @"iPhone", @"iPad"];
[myItems objectAtIndex:1];

Class methods are similar; however, they are called on the class itself rather than an instance of the class. So instead of sending a message to an object created from a class we send the message to the class itself. An example appears directly below.

// Sending a message to the NSArray class
[NSArray array];

The way that we let the compiler know if a method is a class or an instance method is by adding a plus sign (+) for class methods and a minus sign (-) for instance methods in before the method definition. This is also help to let us and other people reading know if a method that we are defining is a class or instance method.

An example of a class method’s syntax:

+ (SomeReturnType)classMethodName

An example of an instance method’s syntax:

- (SomeReturnType)instanceMethodName

Sending Messages

When we call a method on an object we call it “sending a message” to the object. This terminology comes from SmallTalk, the language that Objective-C was heavily included by (you may be familiar this term if you have done Ruby programming).

The way to think of sending the message is an object has some particular functionality defined by a method on the class that instantiated it but it won’t actually perform that action unless a message is sent to it telling it to do so. A real-world example would be sending a friend a text message. You send your friend the message “meet me at 7 Eleven at noon today” via text and if your friend can understand the message he or she can respond by meeting you at 7 Eleven at noon, per your instructions provided they understand your message. If you were to text your friend in a language like Swahili they wouldn’t understand your message and would not be able to respond the instructions in the message you sent them.

Methods work in a similar fashion in that the object that receives the message (your friend in the example given above) won’t be able to respond to the instructions contained in the message unless it understands the message that is being sent. If the object can respond to the message, meaning that the method is defined in the class that created the object or one of its superclasses (we’ll touch more on this in a later post about classes) it will be able to respond to the message by performing the set of instructions defined within the method.

So when we say we’re sending a message to an object in Objective-C we are using methods as our messaging device and the code contained inside of the method’s block (between the curly braces) are the instructions that the object are to follow after receiving the message.

Method Arguments

If we want to pass some external data to a method that it can operate on we can do so by passing arguments, similar to how we do in regular functions. The only difference is that the syntax varies slightly.

When Objective-C was created it was ended to be self-documenting, meaning that by reading the code it should be totally clear what is going on without having to guess the purpose of the code or rely on comments for an explanation. Since methods are used often in Objective-C programs the method names were created to read like sentences written in plain English. For example, if we created an instance method called sayHelloToPerson: we would have to have some way of letting the method know what person to say hello to since our program can have many Person objects in it. The way we do this is by passing a pointer to a person object to the method as an argument. We represent the arguments we are going to pass to the function using a special temporary variable called a parameter when we are declaring and implementing our functions. We would define the method as follows:

// person.h- (void)sayHelloToPerson(Person*)person;

In the example above the “-” indicates that this is an instance method. The return type is void, meaning that this particular method doesn’t return a value; simply logs a message to the console. The name of the function is sayHelloToPerson: and it takes one argument, which is a pointer to a Person object and that argument is represented by a parameter called “person.” The reason that arguments have parameters is because we need some way to represent the value that is being passed into the function if we want to do something with it inside of the function definition.

// person.m- (void)sayHelloToPerson(Person*)person {     NSLog(@"Hello there, %@"., person.name);}

In the example above we are logging the message “Hello there person <insert person’s name here>.” As long as we pass in a pointer to a Person as an argument to this method we can get that person’s name and log it to the console. Another way to think about an argument is that it is a value that we are assigning to a temporary variable (the parameter) that we can use to represent a value that we are passing to the method when we are calling it. If were were to use this method in our program it would looking something like this:

// main.m#import "Person.h"Person *somePerson = [[Person alloc] init];
Person *anotherPerson = [[Person alloc] init];
anotherPerson.name = @"George";[somePerson sayHelloToPerson:anotherPerson]; // "Hello there George." would be printed to the console

In the above example we create two Person objects, one called “somePerson” and one called “anotherPerson.” Assuming that the Person class has property called “name” defined on it we set that value to the NSString “George.” Next, we send the sayHelloToPerson: message to “somePerson” and as a result we log the message “Hello there George” to the console using NSLog. When we send the sayHelloToPerson: message to “somePerson” we pass in “anotherPerson” as the argument to the method so “somePerson” knows which person in our program to say hello to.

The beauty of Objective-C is that the message send in the above example reads almost like a sentence. It literally says “somePerson, say hello to person, anotherPerson.” and the “somePerson” object does exactly that when it receives the message. Though the syntax may look a little funny at first (especially if you’re coming from another programming language like Ruby, JavaScript or Python) once you get used to it you begin to appreciate it.

Methods that Take Multiple Arguments

What if we want to create methods that take multiple arguments? It’s really not that difficult. Let’s create another method on our Person class called addNumbersOne:AndTwo: that gives our person objects the ability to, well as the method’s name implies, add two numbers.

First we include the method’s declaration in the Person class’ header file:

// Person.h- (int)addNumbersOne:(int)firstNumber andTwo:(int)secondNumber;

And then define it inside of our implementation file:

// Person.m- (int)addNumbersOne:(int)firstNumber andTwo:(int)secondNumber {     return firstNumber + secondNumber;
}

In the examples given above “firstNumber” and “secondNumber” are the names of the parameters that represent the arguments that our function takes. Notice how the parameters’ name and type come directly after the “:” in the method declaration and implementation. When a user sends the addNumbersOne:andTwo: message to a person object the values of firstNumber and secondNumber will be whatever values are passed into the function during the message send. We specified that these arguments must be integers so if we try to send the message with values with anything other than an integer we will get an error during compile time. Since we actually have a return statement in this method we have to specify the return type in both the function declaration and implementation. In this case it’s an integer so if our method were to return anything other than an integer we would get an error at compile time. If we were to call this method in an actual program it would look as follows:

// main.mPerson *accountant = [[Person alloc] init];int sum = [accountant addNumbersOne:3 andTwo:4];NSLog(@"%i", sum); // 7 would be printed to the console

In this case we create a new Person object called “accountant” and send the addNumbersOne:andTwo: along with the arguments 3 and 4 to it. The “accountant” object responds by adding the two numbers and returning the sum, which we store in a variable called “sum.” We then log the sum to the console using the NSLog function.

How to Easily Distinguish Between Function and Method Calls

Any easy way to tell if we are calling a function or a method is to look at how it is invoked (fancy programming word for called) in the program. If we see a call with square brackets around it we know that a method is being called, if not it’s a function call.

// Calling a function that adds two numbers
add(1, 2) // 3 would be returned
// Calling a method that adds two numbers
[someObject addNumbers:1
and:2;]; // 3 would also be returned

Notice how we can make it clear what our arguments’ values are by lining them up by semicolon. This is a common practice in Objective-C and is especially useful if you are invoking a method that takes many arguments.

Summary

Hopefully after reading this article you have a better understanding of functions and methods, how to create them and how and when to use them in your programs. In my next post we’ll talk more about classes, inheritance and how we can use them to better organize our code.

--

--