Logo
blank Skip to main content

Objective-C Quick Tutorial for Beginners: Learn Objective-C Basics and Specifics from Scratch

Anyone who wants to develop programs for the products of Apple Company, i.e. for MacOS and iOS platforms, meets such a moment in his life when he needs to learn new programming language – Objective-C. I met such moment once too and asked myself how to learn Objective-C fast. I decided to take notes of my thoughts when understanding the documentation to remember the main specifics of this language – this is how this beginner’s guide to Objective-C appeared. This is probably not the best Objective-C tutorial for beginners, but I hope it will allow you to save time and efforts starting your work with this language.

Banal Theory of Origin of OOP

The problem of reuse of the written code and its portability always makes the developers search for new ways of its sorting, organization, and abstracting. New programming paradigms, design patterns, languages, compilers and standard libraries for them, program platforms and frameworks are created to solve these problems. In such a way, a paradigm of subprograms (procedures) appeared. It is implemented with the help of CALL\RET processor commands and stack (in fact, it is redirection of the execution thread by the address of an arbitrary command but not the one following the current command, with the subsequent return). Then, there is a paradigm of modules (each file is a separate translation unit) that created a two-stage translation: compilation of modules and then their linking (static and dynamic) to an executable module. As a result of increasing of the code size in projects and difficulties of its support, a new object โ€“ oriented paradigm of programming appeared in early 1960s. This paradigm splits programs into even smallest components โ€“ types of data. Its main point consists in interaction of entities (objects) by means of sending messages to each other. Each object is a variable of a data type defined by the developer (the so-called class). The definition of such special user data type (class) consists in the following two things: definition of data set (invariants, members) and a set of subprograms (methods) that will serve them.

i2

Class is usually designed as a type defined by the developer. This type is based on embedded (language) data types and/or other classes. This can be a structure (struct) for the C language that does not support an object-oriented paradigm. A set of subprograms is implemented as usual functions that have at least one parameter โ€“ pointer to a data set that is to be handled. The main advantage of the object-oriented method is the possibility to create new classes on the basis of the written ones (to add invariants and methods, redefine methods, use methods defined in the base class as their own). It is called inheritance.

i3

A set of methods represents an interface for interaction with invariants. Impossibility of direct modification of class data (without using its interface) reflects the principle of encapsulation. On the figure above, class and its oblects are represented. There is an x invariant of float type and doubleX interface (method) for it that returns the invariant value. Sometimes it is necessary to send message to an object that responds to it definitely (i.e., to call the method  implemented for this class object), but the certain class of this object is unknown according to cicumstances. For example, we need to send a Move message to each element of the list of pointers to objects of Auto class. And we know that there are pointers to objects of not only Auto class in the list but there are also pointers to derivative (inherited) Ford and Subaru classes. We can do it only due to the polymorphism principle. It consists in the following:  when a certain message is sent to an object from a certain class hierarchy, where all objects can receive such message, this object responds to it correspondingly to its own class but not the basic class for the current hierarchy. Simula 67 became the first language with the support of the object-oriented approach. Then, Smalltalk appeared. And in 1980s, C++ appeared, the main language of the modern system programming. Its expansion and improvement in 1990s generated a number of design paradigms and patterns. It also influenced the modern vision of object-oriented approach, including Objective-C language.

A Little of History

Objective-C appeared in 1980s as a modification of C in Smalltalk โ€œstyleโ€. This modification consisted in adding the new syntactic constructions and in a special preprocessor for them (which transformed them into the simple calls of C functions in the code), and also in the runtime library (to process those calls). Thus, Objective-C was initially interpreted as an add-on over C. In some way, it is still so: we can write a program in pure C and then add some constructions from Objective-C (if necessary). Or vice versa: we can freely use C in programs in Objective-C. Besides, all of this also concerns programs in C++.We can say that the easiest way to learn Objective-C from scratch is already have knowledge of C/C++ :).

In 1988, NeXT (later, Apple) licensed Objective-C and wrote a compiler and a standard library (SDK in fact) for it. In 1992, the developers of the GNU project within the OpenStep project improved of the language and compiler. Since then, GCC has supported Objective-C. After buying NeXT, Apple took their SDK (compiler, libraries, IDE) as a basis for their further implementations. IDE for the code was named Xcode and IDE for GUI was named Interface Builder. Cocoa framework for GUI implementations (and not only for them) is the most significant development environment for programs in Objective-C nowadays, so learning Cocoa is a must.

Specifics of Objective-C

Module files in Objective-C have the โ€œ.mโ€ extension (if the mix of C++ and Objective-C was used, then the extension is โ€œ.mmโ€). Header files have the โ€œ.hโ€ extension. All class objects created in Objective-C must be allocated in the dynamic memory. That is why, id type takes special significance. This type is a pointer to an object of any class (void * in fact). The zero pointer is named by the nil constant. Thus, we can convert the pointer to any class to the id type. A problem appears: how can we know to which class the object that is hidden behind id refers? It is made using the isa invariant that is present in any class object that inherited the special NSObject basic class (NS prefix means NeXT Step). The isa invariant refers to the reserved Class type. The object of such type allows to find out names of its own and basic class, set of class invariants, and also prototypes of all methods that this object implements and their addresses (by means of the local list of selectors). All reserved Objective-C words that differ from the reserved C language words start with the @ symbol (for example, @protocol, @selector, @interface). Usually, names of class invaiants with the limited scope (@private, @protected) start with the underscore character. There is a convenient NSString class for strings in Cocoa. String constant of such class is written as @โ€Hello worldโ€ and not as a simple C string constant โ€œHello worldโ€. The BOOL type (in fact, unsigned char) can accept YES and NO constant values. All reserved words specific for Objective-C (that differ from C language and are located in the objc/objc.h header file) are listed below:

  • @interface โ€“ Starts the declaration of the class or category (category is an extension of the class by additional methods without inheritance)
  • @implementation โ€“ Starts the definition of the class or category
  • @protocol โ€“ Starts the declaration of a protocol (analog of C++ class that consists of only virtual functions)
  • @end โ€“ Ends the declaration\definition of any class, category, or protocol
  • @private โ€“ Limits the scope of visibility of class invariants for the class methods (similarly to C++)
  • @protected โ€“ is set by default. Limits the scope of visibility of class invariants for the class methods and methods of derivative classes (similarly to C++)
  • @public โ€“ Deletes the limitations for the scope of visibility (similarly to C++)
  • @try โ€“ Defines the block with a possible generation of exceptions (similarly to C++)
  • @throw โ€“ Generates an object-exception (similarly to C++)
  • @catch () โ€“ Handles the exception generated in the previous @try block (similarly to C++)
  • @finally โ€“ Defines the block after the @try block, to which the control is passed, regardless of the fact if the exeption was generated or not
  • @class โ€“ Shortened form of class declaration (only the name; similarly to C++)
  • @selector(method_name) โ€“ Returns the compiled selector for the name of the method_name method
  • @protocol(protocol_name) โ€“ Returns the copy of the class-protocol with the protocol_name name
  • @encode(type_spec) โ€“ Initializes the string of symbols that will be used for encryption of data of type_spec type
  • @synchronized() โ€“ Defines the code block that is executed only by one thread at any certain moment

Message Exchnage

To make the object execute a certain method, we need to send it a message with the same name as the required method. Such message is called method selector. Sending syntax is as follows:

ShellScript
[receiver  method];
i4

We can send parameters for the called method in the message:

ShellScript
[receiver  method: 20.0ย : 30.0];

We need to put a colon before each parameter. There are as many colons as parameters. The name of the method can continue after each such colon-parameter:

ShellScript
[receiver  methodWithFirstArgument: 10 andSecondArgument: 20];

Methods with unlimited amount of arguments are called by the following syntax:

ShellScript
[receiver  undefinedNumberParameters: one, two, three, four, five, six, seven];

Message sending (as any C function) returns a certain value (perhaps, void):

Objective-C
BOOL  booleanValue; booleanValue = [reveiver method];

It disappears when sending the nil message. An exception appears when sending a message to an object that belongs to a class that did not implement the required method. This exception, being not intercepted, leads the whole program to an unexpected shutdown. We can use the following code pattern to check if the current object responds to any message:

Objective-C
if  ([anObject respondsToSelector: @selector(myMethodWith2Argumets::)])
  { //we can  call
  [anObject  myMethodWith2Argumetns: @โ€firstโ€ย : @โ€secondโ€];
  }
  else
  {
  //do not  call in any case
  }

The Work of Message Transfer

Sending a message is translated into the C-function with a prototype:

Objective-C
id  objc_msgSend(id receiver, SEL method, โ€ฆ);

The SEL type is defined as char const * in fact. But it is better to interpret it as int because all selectors are indexed by integer values according to the global table of selectors during the execution.

i5

Using the isa invariant of the receiver object (the presence of isa is inevitable because all classes must inherit the NSObject class when using Foundation framework, basic for Cocoa), this function browses the local list of class selectors to define if an object of the current class responds to the method message. If there is such selector, the control is passed to the corresponding class method. The object id (pointer to its invariants) and parameters of the objc_msgSend() function defined after the selector are passed to this class method. The value returned by the method is returned as a result of message sending. If the object-receiver does not have such selector, the objc_msgSend() function browses the list of selectors of its basic class.

i6

For example, using such scheme, the call:

Objective-C
[receiver  addObject: otherObject];

Is translated into:

Objective-C
objc_msgSend(receiver,  12, otherObject);

In the global table of selectors, 12 corresponds to the โ€œaddObject:โ€ string. Next, the objc_msgSend() function performs the search in the list of selectors of the receiver object. When the function finds it (let it be an object of the NSArray class, which implemented the method with 12 selector), it performs the call of the type: addObject(receiver, otherObject);

Objective-C Method Declaration

It is interesting to note that the prototype of the addObject method from the previous part looked as follows in the Objective-C class declaration:

Objective-C
(void)addObject: (id)otherObject;

It accepted only one parameter. Proceeding from the principle of object-oriented paradigm that methods are subprograms that handle certain data sets we need to pass address of data to be processed to the method. That is why such parameter is passed to any class method indirectly. The minus sign (โ€œ-โ€œ) that stands first in the method prototype informs the compiler about the additional parameter. Such method (with the minus sign before) is called the object method (or instance method). It can be called only for the object of any class. In the method body, this pointer to the copy of data (or the object address to which the message was sent) is available by means of the reserved self word (similarly to this in C++). The pointer to the instance of the basic class is available through the reserved super word. Besides, an indirect _cmd parameter โ€“ selector of this method from the global table of selectors โ€“ is passed to the object method. From the C++ developer point of view, all object methods in Objective-C are like those declared with the virtual key word and always follow the dynamic polymorphism. If we put the plus sign (โ€œ+โ€) in the beginning of the method prototype, such method will be interpreted as class method and will not accept the indirect self parameter, correspondingly (it is similarly to the declaration of static-method in C++). The super pointer will not also work without the isa object invariant to which self points. Thus, the prototype of any method is declared in the following way:

Objective-C
  -|+ (<type  of the returned value>) main PartoftheMethodName [ย : (<type of the  first parameter>)name of the FirstFormalParameter [ [additional  PartoftheMethodName]ย : (<type of the second parameter>)name of the  SecondFormalParameter] โ€ฆ ]

For example:

Objective-C
+ (Class)class;
+ (id)alloc;
- (id)init;
- (void)addObject: (id)anObject;
+ (NSString *)stringWithCString: (const char*)aCString usingUncoding: (enum NSStringEncoding)encoding;
- (NSString *)initStringWithFormat: (NSString *)format, โ€ฆ;

If the method returns a certain object (id type) or class (Class type), we can use the embedded syntax of the call:

Objective-C
[myLabel  setText: [[NSString stringWithString: @โ€Helloโ€] stringByAppendingString: @โ€  worldโ€]];

Here, value of the text invariant (that is equal to a @โ€Hello worldโ€ string) is set for the object of the UILabel class from the UIKit framework. This string in its turn is created by the concatenat ion of @โ€Helloโ€ and @โ€ worldโ€ strings. The first is a result of sending of stringWithString message to a NSString class with a @โ€Helloโ€ parameter-constant. Such call returns the object of the NSString class that is initialized by a string-parameter. Then, the stringByAppendingString message with @โ€ worldโ€ parameter is sent to this method. The result of sending this message is the object of the NSString class that contains concatenation of the value of the object-receiver and string argument. This object gets as a parameter to the setText: message of the myLabel object.

Objective-C Class Declaration

Let’s proceed to a small Objective-C classes tutoral. Letโ€™s declare a simple class for the complex number in the Complex.h file:

Objective-C
#import <Foundation/Foundation.h> //for NSObject and NSString strings
@interface Complex : NSObject
{
double _re; //invariant for a real part
double _im; //invariant for an imaginary part
NSString *_format; //format string for the description method
}
- (id)initWithRe: (double)re andIm: (double)im; //specialized constructor
+ (Complex *)complexWithRe: (double)re andIm: (double)im; //class method for a one-stage creation of an object
- (Complex *)add: (Complex *)other; //method for addition
- (Complex *)sub: (Complex *)other; //method for substraction
- (NSString *)format; //method of access to _format
- (void)setFormat: (NSString *)format; //method of _format setting
- (double)re; //other methods of access to real and imaginary parts
- (void)setRe: (double)re;
- (double)im;
- (void)setIm: (double)im;
@end

As we can see, the whole declaration is put into key words @interface and @end. First, invariants are declared (in curly brackets). Methods are placed out of curly brackets. The description method is absent in the class declaration not accidentally. It is present in the class definition as well as the dealloc and init method. When sending a description message to an object of the Complex class, its local list of selectors will be examined. After compilation, selectors of all methods implemented by the class of this object and even not declared in the interface part will get into this list. It means that init, description, and dealloc will be called correctly.

Creation of Objects in Objective-C

All objects are allocated in the dynamic memory, that is why we need to create an object in two steps:

  1. memory allocation (alloc message) and
  2. initialization of invariants (class constructors).
Objective-C
MyClass  *myObject = [[MyClass alloc] init]; //method of the MyClass alloc class  allocates a chunk of required size and returns the pointer to it, method of the  init object initializes invariants of the myObject object

After the creation of an object, we can use it:

Objective-C
NSMutableArray  *array = [[NSMutableArray alloc] init]; //we create a variable array
  MyClass  *myObject = [[MyClass alloc] init]; //our object [myObject myMethod]; //sending  of a certain message
  [array  addObject: myObject]; //we place the object to the array
  MyClass  *otherObject = [array getLastObject:]; //we take it from the array, point to it  using another pointer
  [otherObject  myOtherMethod: YES]; //we send it another message with an argument of the BOOL  type

Some classes possess a method for quick (one-stage) creation of their own instances. Such methods are class methods, they return a pointer to an object of their own class and their name usually starts with the name of the class itself. For example, the following method:

Objective-C
+  (NSString *)stringWithCString: (char const *)string encoding:  (NSStringEncoding)encoding;

It returns the ready string that is initialized by the corresponding string with a final zero without calls of alloc and init:

Objective-C
NSString  *myString = [NSString stringWithCString: โ€œBla-bla-blaโ€ encoding:  NSASCIIStringEncoding];

Object Lifetime

As soon as the pointer to an object goes beyond the scope, memory that is allocated for it is lost (if that is the last pointer to that object) and leaks. The paradigm of calculation of references to resources is supported in Objective-C to avoid such unwanted effects. Thus, each object has an integer counter that displays the number of pointers that refer to it. When this counter reaches zero, memory allocated for this object returns to the system. This counter is equal to 1 after the call of the method of the alloc class. We need to send a retain message to an object to increase its value and release message to decrease the value. NSObject implements all these methods. Any our class inherits it. It is interesting to note that value of the counter for static objects of the NSString class (for example, @โ€I am a stringโ€) is equal to -1, i.e., the maximum possible. Here is an example of work with the counter:

Objective-C
id anObject = [SomeClass alloc]; //first, the counter == 1
anObject = [anObject init]; //here, object invariants are created
[anObject reatin]; //we increase its value (now, it is equal to 2)
[anObject release]; //then, we decrease it (the counter is equal to 1 again and the object is still viable)
[anObject release]; //the counter is zeroed; invariant counters are decresed by 1 and memory allocated for the object is returned to the OS

Implementation of init is very important. It is the class constructor. Constructors differ in that they return id and their names always start with the word init, and the constructor by default is init itself. The scheme of any constructor is like the one below:

Objective-C
- (id)init
{
  self = [super init]; //we call the constructor of a basic class for initialization of its invariants
  if (self) //if everything succeeded in the constructor of a basic class and it returned a correct object and did not return nil having released memory
  {
    //we can initialize our invariants
  }
  return self; //and return itself
}

Here is a typical specialized (not default) constructor for a class with two members of a type of a certain class and with one integer invariant:

Objective-C
- (id)initWithInt: (int)number
{
  if (self = [super init])
  {
    _myMember1 = [[SomeClass alloc] init]; //we allocated memory and then initialized it
    _myMember2 = [[SomeClass alloc] init];
    _myIntMember = number; //here, we initialize a constructor by a passed parameter โ€“ just for example
  }
  return self;
}

Implementation of release and retain for NSObject is ideologically as the shown below and we do not need to redefine it in derivative classes due to absence of access to the invariant of references counter:

Objective-C
- (void)retain
{
  [_internalLock lock]; //locking for synchronization
  _referenceCounter++; // supposing that _referenceCounter is a hidden invariant of a counter
  [_internalLock unlock];
}
- (void)release
{
  [_internalLock lock];
  _referenceCounter--; //we decrease the counter
  if (!_referenceCounter) //if it is equal to zero
  {
    [_internalLock unlock];
    [self dealloc]; // (here, locking will release)
  }
  [_internalLock unlock];
}

It means that dealloc message is sent to the object itself. In the implementation of its method it can decrease counters of its invariants if necessary and pass the similar message to an object of a basic class so that it makes the same. It is obvious that implementation of a dealloc method for NSObject will release memory allocated for the object. Usually, dealloc for a certain class looks as follows:

Objective-C
- (void)dealloc
{
  [_myMember1 release]; //we decrease the counter of our invariant
  [_myMember2 release]; //we decrease the counter of another our invariant
//[_myIntMember release]; it does not have any sense; embedded types do not receive messages at all and do not have counters
  [super dealloc]; //letโ€™s tell an object of a basic class that itโ€™s time to release memory
}

Access Methods

A correct work with the calculation of references is very important when returning the object address from the method or when initializing invariant using a formal parameter. Usually, the so-called access methods are used for such things. They return and set invariants of objects. It is good practice to name the method that returns the invariant value in the same way as the invariant and to start the name of the method that sets its value with the set word:

Objective-C
- (void)setRe: (double)re
{
  _re = re;
}

The _re invariant refers to the embedded type so there will be no difficulties with change of its values. But if the invariant is an object of a certain class, a simple assignment is not enough because we need to take into account reference counters. The following three methods are applied to solve this problem:

Objective-C
//for example, we need to change the label text
[label setText: @"Hello world"]; //we set the text invariant
//of a label object equal to text constant of NSString * type
//approximate implementation of setText in UILabel class (variant โ„–1)
- (void)setText: (NSString *)text
{
  [text retain]; //we increase the reference counter to a formal parameter
  [_text release]; //we decrease the reference counter of the current value of our _text invariant
  _text = text; //we initialize the invariant with a new value
}
//approximate implementation of setText in UILabel class (variant โ„–2)
- (void)setText: (NSString *)text
{
  if (_text != text) //we compare pointers to objects
  {
    [_text release]; //we decrease the reference counter of the current value of our _text invariant
    _text = [text retain]; //we increase the reference counter to a formal parameter and initialize our invariant
  }
}
//approximate implementation of setText in UILabel class (variant โ„–3 โ€“ undesirable)
- (void)setText: (NSString *)text
{
  if (_text != text)
  {
    [_text autorelease]; //we throw the current value of our _text invariant to a autorelease pool 
    _text = [text retain]; //we increase the reference counter to a formal parameter and initialize our invariant
  }
}

Variant โ„–3 is not very good because it obstructs the current autorelease pool and it is usually undesirable (see the next part). Method of access for reading the value of invariant is always very simple:

Objective-C
- (NSString *)text
{
  return _text;
}

Autorelease Pool in Program Threads

Now, letโ€™s try to return an object created inside the method:

Objective-C
- (NSString *)sayHelloToName: (NSString *)name withSurname: (NSString *)surname
{
  NSString *retString = [[NSString alloc] initWithFormat: @โ€%@ %@!โ€, name, surname]; //we initialize the created object by means of the return retString format string;
}

The format string corresponds to C language standard. But if we need to define the id type in it, we use the format specificator – %@. How does the method that parses the format understand, which symbols to place instead of id? It will just place what the description method of the current object will return. This method is initially declared for the NSObject class. NSString redefines it to the output of its string contents. Having redefined it, any object can represent its string contents. For example, the class of a complex number with two invariants of the double type can perform this as follows:

Objective-C
- (NSString *)description
{
  return [NSString stringWithFormat: @โ€re: %lf im: %lfโ€, _re, _im]; //it returns the @โ€re: 1.0 im: 2.5โ€ string for _re == 1.0 and _im == 2.5
}

There will be a memory leak after performing the sayHelloToName:withSurname: method. The calling code will not guess that the release message must be sent to the returned object after processing. Even if it performs that, it can happen that the pointer to object invariant is returned and it means that its deletion can have serious consequences. It would be great to have a mechanism of self-release of objects in future so that the user code did not need to release them. This problem can be solved with the help of an object of the NSAutoreleasePool class โ€“ autorelease pool of objects. We can send the autorelease message to all objects created after the creation of an object of such class. Such object is placed to the current (the last created) autorelease pool. When a certain pool receives the release message, it sends the same message to all its objects. It also decreases their reference counter (in fact, deleting it). Thus, an object placed into the autorelease pool continues living and occupies memory during the whole pool life. It is convenient for small temporary objects but it can occupy a significant part of the memory with the course of time. That is why it is recommended to frame cycles that can create a lot of temporary objects, which move to the autorelease pool, with the local (embedded) pools. Any thread in the program that uses Cocoa must create an object of the NSAutoreleasePool class in the beginning (before creating other objects) and delete it at the end (after deleting all other objects). The main() function that is the main thread of any program in Objective-C must look like this when using the Cocoa framework:

Objective-C
int main(int argc, char *argv[]) // or just main()
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; //we create a pool and it becomes the current one automatically
  int retVal; //now we can develop
  [pool release]; //we release the pool and all objects that are placed to it using the autorelease calls
  return retVal;
}

And the correct sayHelloToName:withSurname: method will look like this:

Objective-C
-(NSString *)sayHelloToName: (NSString *)name withSurname: (NSString *)surname
{
  NSString *retString = [[NSString alloc] initWithFormat: @โ€%@ %@!โ€, name, surname]; //we initialize the created object by means of the [retString autorelease] format string; then we place it to the pool and retString will be released together with the pool
  return retString;
}

Besides, the drain method of the autorelease pool is similar to release. The only difference is that except the release of itself and other objects contained in it, it prompts the garbage collector to go in. But it is actual only for Mac OS 10.4 and higher because iOS does not have the garbage collector.

Objective-C Class Definition

Now, letโ€™s examine the Complex.m file with the definition of methods of the Complex class:

Objective-C
#import โ€œComplex.hโ€
@implementation Complex
- (id)init
{
  return [self initWithRe: 0.0 andIm: 0.0];
}
- (id)initWithRe: (double)re andIm: (double)im
{
  if (self = [super init])
  {
    _re = re;
    _im = im;
    _format = @โ€re: %.1lf im: %.1lfโ€; //output format by default
  }
}
+ (Complex *)complexWithRe: (double)re andIm: (double)im
{
  return [[[Complex alloc] initWithRe: re andIm: im] autorelease];
}
- (Complex *)add: (Complex *)other
{
  return [[Complex alloc] initWithRe: _re + other->_re andIm: _im + other->_im];
}
- (Complex *)sub: (Complex *)other
{
  return [[Complex alloc] initWithRe: _re โ€“ other->_re andIm: _im โ€“ other->_im];
}
- (NSString *)format
{
  return _format;
}
- (void)setFormat: (NSString *)format
{//standard order of actions for invariant-object
  [format retain];
  [_format release];
  _format = format;
}
- (double)re
{
  return _re;
}
- (void)setRe: (double)re
{
  _re = re;
}
- (double)im
{
  return _im;
}
- (void)setIm: (double)im
{
  _im = im;
}
- (NSString *)description
{//we use the set output format
  return [NSString stringWithFormat: _format, _re, _im];
}
- (void)dealloc
{
  [_format release]; //dealloc was redefined for this
  [super dealloc];
}
@end

By default, the constructor calls the specialized constructor with the defined initial parameters. The complexWithRe:andIm: method returns the initialized object of the Complex class  that is located in the current autorelease pool. The description method performs the same and returns the object of the NSString class. Here is an example of a program where the Complex class is used:

Objective-C
#import โ€œComplex.hโ€
#import <stdio.h>  //for printf()
int main()
{
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  Complex *num1 = [[Complex alloc] init]; //0.0+0.0*i
  Complex *num2 = [[Complex alloc] initWithRe: 1.5 andIm: -2]; //1.5-2.0*i
  Complex *num3 = [Complex complexWithRe: 5 andIm: 7]; //5.0+7.0*i
  printf(โ€œ%s\nโ€, [[num2 description] cStringUsingEncoding: NSASCIIStringEncoding]); //output> re: 1.5 im: -2.0
  printf(โ€œ%s\nโ€, [[[num2 add: num3] description] cStringUsingEncoding: NSASCIIStringEncoding]); //output> re: 6.5 im: 5.0
  [num1 setRe: [num2 re]]; //set  _re for num1 as in num2
  [num1 setIm: [num3 im]]; //set _im for num1 as in num3
  [num1 setFormat: @โ€%.2lf+%.2lf*iโ€]; //change the output format for num1
  printf(โ€œ%s\nโ€, [[num1 description] cStringUsingEncoding: NSASCIIStringEncoding]); //output> 1.50+7.00*i
  [num1 release];
  [num2 release];
//[num3 release]; it is not required as it is already in the autorelease pool
  [pool drain];
  return 0;
}

Categories and Extensions

Categories help to add/redefine some methods without inheritance from an already written (or, maybe, compiled one) class:

Objective-C
//file โ€œCategorizedComplex.hโ€
#import โ€œComplex.hโ€
@interfce Complex (CategorizedComplex)
- (Complex *)mul: (Complex *)other;
- (Complex *)div: (Complex *)other;
@end
//file โ€œCategorizedComplex.mโ€
#import โ€œCategorizedComplex.hโ€
@implementation Complex (CategorizedComplex)
- (Complex *)mul: (Complex *)other
{
  return [Complex complexWithRe: _re * other->_re - _im * other->_im andIm: _re * other->_im + _im * other->_re];
}
- (Complex *)div: (Complex *)other
{
  double retRe, retIm, denominator;
  denominator = other->_re * other->_re + other->_im * other->_im;
  if (!denominator)
    return nil;
  retRe = (_re * other->_re + _im * other->_im) / denominator;
  retIm = (_im * other->_re - _re * other->_im) / denominator;
  return [Complex complexWithRe: retRe andIm: retIm];
}
@end

And we can use it as follows:

Objective-C
Categoriz?dComplex *num1 = [[CategorizedComplex alloc] initWithRe: 1 andIm: 999];
Complex *num2 = [Complex complexWithRe: 0 andIm: 0];
CategorizedComplex *num3 = [num1 div: num2]; //num3 == nil

Extensions serve as anonymous categories:

Objective-C
//file โ€œCategorizedComplex.mโ€
#import โ€œCategorizedComplex.hโ€
@interface Complex ()
  - (void)zeroComplex; //secret method for zeroing of a number
@end
@implementation Complex
- (void)zeroComplex //only methods of the class itself can use it
{
  _re = 0;
  _im = 0;
}
@end

Protocols

The Objective-C protocol is a formalized declaration of a group of methods that can implement any class if necessary (similar to class in C++ where all methods are declared with the virtual โ€ฆ = 0 specificator). There can be required (@required specificator that is considered as a default one) and selective (@optional specificator) protocol methods in the language version 2.0. If any class implemented the required protocol methods, it is called a class that supports this protocol. Protocol and class that supports it are declared as follows:

Objective-C
@protocol MyPrinterProtocol
@required
- (void)print;
- (BOOL)switchedOn;
@optional
- (void)loadPapaer: (int)numberOfPages;
@end
@interface MyPrinter : NSObject <MyPrinterProtocol>
//now, MyPrinter implements MyPrinterProtocol methods
{
  BOOL _state;
  int _numberOfPages;
}
- (id)initWithState: (BOOL)state andPagesCount: (int)pages;
- (BOOL)state;
@end

We can send the print and switchedOn messages to an object of the MyPrinter class. After checking for respondsToSelector:, we can send the loadPaper: message as there must be definitions of the similar methods in its implementation. The declaration of an object of a class that supports a certain protocol is performed in as follows:

Objective-C
MyPrinter  *printer; id anotherPrinter = [[MyPrinter alloc] init]; [anotherPrinter print];  //anonymous object responds to the message without compiler warning

Besides, one class can support several protocols. For this, we can enumerate them using commas in brockets in the class declaration.

Objective-C
@interface  MyPrinterย : NSObject <MyPrinterProtocol, OtherProtocol>

We should write such line to declare an object of an unknown class (id) that corresponds to a certain protocol:

Objective-C
id  <MyPrinterProtocol> somePrinter;

Exceptions in Objective-C

There are two main approaches for errors processing: global status variable, whose value informs about the success of execution of a previous operation, and generation of exceptions. Their main point is: the code where an error occured expects that this error can be solved by the code that called it. That is why it returns the control to this code and informs about the situation that happened in detail. Objective-C supports both these approaches. Exception is an object of a certain class. It carries certain information about the occured situation (even by its type). There is an NSException class in Cocoa that can be initialized using two NSString objects and one object of any class (id type):

Objective-C
-  (id)initWitnName: (NSString *)name reason: (NSString *)reason userInfo:  (id)userInfo;

We can generate exceptions and start the mechanism of unwinding of call stack with the help of the @throw operator. To intercept the generated exception, we need to enclose the part of the code where generation is possible in a special block with a @try heading (such blocks can be embedded). Then, after this block, we need to place a block with a @catch() heading where we need to define the type of the supposed exception in round brackets. There can be several @catch() blocks after the @try block. After the exception generation, control unwinds stack and leaves the @try block. Then it checks all @catch() blocks one by one and gets into the @catch() block in whose curly brackets the type, which the exception type is converted to indirectly (exact match, pointer to basic class or id), stands. If an exception by type did not match any @catch() block, control continues the stack unwinding. If there is a block with @finally heading after the block with a @try heading, control is passed to it independently if exception occurred in the @try block (and some @catch() block is processed), or its last instruction is executed. Here is an example of work with an object of Cup class (exception occurs in its fill method):

Objective-C
Cup *cup = [[Cup alloc] init];
@try
{
  [cup fill]; //an exeption of the NSException type is generated to fill
}
@catch (NSException *exception)
{//we log the occurred exception with the help of NSLog
  NSLog(@"main: Caught %@: %@", [exception name], [exception reason]);
}
@finally //we get here after @try
{
  [cup release];
}

In the @finally block, it is good to release resources that are allocated in the @try block but that are not released due to generated exception.

Properties

Our implementation of the Complex class is redundant for Objective-C 2.0. There are a lot of access methods in it and their definition is a rut. Letโ€™s rewrite it using the properties:

Objective-C
//โ€œComplex.hโ€ file
#import <Foundation/Foundation.h>  //for NSObject and NSString strings
@interface Complex : NSObject
{
  double _re; //invariant for a real part
  double _im; //invariant for an imaginary part
  NSString *_format; //format string for the description method
}
- (id)initWithRe: (double)re andIm: (double)im;
+ (Complex *)complexWithRe: (double)re andIm: (double)im;
- (Complex *)add: (Complex *)other; //method for addition
- (Complex *)sub: (Complex *)other; //method for substraction
@property (nonatomic, retain) NSString *format; //we declare access methods
@property (nonatomic, assign) double re; //by means of declaration of properties
@property (nonatomic, assign) double im;
@end
//file โ€œComplex.mโ€
#import โ€œComplex.hโ€
@implementation Complex
@synthesize format = _format; //we generate access methods
@synthesize re = _re; //and rename them at the same time
@synthesize im = _im; //so that there is no underlining in the name
- (id)init
{
  return [self initWithRe: 0.0 andIm: 0.0];
}
- (id)initWithRe: (double)re andIm: (double)im
{
  if (self = [super init])
  {
    _re = re;
    _im = im;
    _format = @โ€re: %.1lf im: %.1lfโ€; //output format by default
  }
}
+ (Complex *)complexWithRe: (double)re andIm: (double)im
{
  return [[[Complex alloc] initWithRe: re andIm: im] autorelease];
}
- (Complex *)add: (Complex *)other
{
  return [[Complex alloc] initWithRe: _re + other.re andIm: _im + other.im]; //we use re and im properties
}
- (Complex *)sub: (Complex *)other
{
  return [[Complex alloc] initWithRe: _re โ€“ other.re andIm: _im โ€“ other.im]; //we use re and im properties
}
@end

Property is a certain name that is available via the pointer to an object by means of point operator โ€œ.โ€. Properties are used instead of access methods to get or set the object invariant. A row of parameters that describe specifics of access methods generated by a property are defined during the declaration of a property.

  • getter=getterName, setter=setterName – Defines that the access method for reading will be called getterName and for changing – setterName
  • readonly โ€“ Not to generate the access method for changing
  • readwrite โ€“ To generate both access methods
  • assign โ€“ To generate the access method for changing by means of simple assignment
  • retain – To send the retain message to the accepting value; to send the release message to the previous invariant value and to assign the accepted value to it
  • copy – To use a simple assignment operator but to assign the copy of the accepted value (before assignment, the copy message is sent to it)
  • nonatomic โ€“ Not to use internal lockings for synchronization of several threads in generated access methods (synchronization is used by default)

Now, we do not need to write access methods manually in the definition of the Complex class. They will be generated by a compiler and will be identical to those that were before.

And of course,this theoretical Objective-C quick tutorial is good, but still the best way to learn Onbjective-C as any other programming language is practice. So, install Xcode quicker and proceed to the xCode tutorial ๐Ÿ™‚

Have a question?

Ask our expert!

Tell us about
your project

...And our team will:

  • Process your request within 1-2 business days.
  • Get back to you with an offer based on your project's scope and requirements.
  • Set a call to discuss your future project in detail and finalize the offer.
  • Sign a contract with you to start working on your project.

Do not have any specific task for us in mind but our skills seem interesting? Get a quick Apriorit intro to better understand our team capabilities.