The main purpose of Objective-C programming language is to add object orientation to the C programming language and classes are the central feature of Objective-C that support object-oriented programming and are often called user-defined types.
A class is used to specify the form of an object and it combines data representation and methods for manipulating that data into one neat package. The data and methods within a class are called members of the class.
Objective-C characteristics
- The class is defined in two different sections namely @interface and @implementation.
- Almost everything is in form of objects.
- Objects receive messages and objects are often referred as receivers.
- Objects contain instance variables.
- Objects and instance variables have scope.
- Classes hide an object's implementation.
- Properties are used to provide access to class instance variables in other classes.
Objective-C Class Definitions:
When you define a class, you define a blueprint for a data type. This doesn't actually define any data, but it does define what the class name means, that is, what an object of the class will consist of and what operations can be performed on such an object.
A class definition starts with the keyword @interface followed by the interface(class) name; and the class body, enclosed by a pair of curly braces. In Objective-C, all classes are derived from the base class called NSObject. It is the superclass of all Objective-C classes. It provides basic methods like memory allocation and initialization. For example, we defined the Box data type using the keyword class as follows:
@interface Box:NSObject { //Instance variables double length; // Length of a box double breadth; // Breadth of a box } @property(nonatomic, readwrite) double height; // Property @end
The instance variables are private and are only accessible inside the class implementation.
Allocating and initializing Objective-C Objects:
A class provides the blueprints for objects, so basically an object is created from a class. We declare objects of a class with exactly the same sort of declaration that we declare variables of basic types. Following statements declare two objects of class Box:
Box box1 = [[Box alloc]init]; // Create box1 object of type Box Box box2 = [[Box alloc]init]; // Create box2 object of type Box
Both of the objects box1 and box2 will have their own copy of data members.
Accessing the Data Members:
The properties of objects of a class can be accessed using the direct member access operator (.). Let us try the following example to make things clear:
#import <Foundation/Foundation.h> @interface Box:NSObject { double length; // Length of a box double breadth; // Breadth of a box double height; // Height of a box } @property(nonatomic, readwrite) double height; // Property -(double) volume; @end @implementation Box @synthesize height; -(id)init { self = [super init]; length = 1.0; breadth = 1.0; return self; } -(double) volume { return length*breadth*height; } @end int main( ) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Box *box1 = [[Box alloc]init]; // Create box1 object of type Box Box *box2 = [[Box alloc]init]; // Create box2 object of type Box double volume = 0.0; // Store the volume of a box here // box 1 specification box1.height = 5.0; // box 2 specification box2.height = 10.0; // volume of box 1 volume = [box1 volume]; NSLog(@"Volume of Box1 : %f", volume); // volume of box 2 volume = [box2 volume]; NSLog(@"Volume of Box2 : %f", volume); [pool drain]; return 0; }
When the above code is compiled and executed, it produces the following result:
2013-09-22 21:25:33.314 ClassAndObjects[387:303] Volume of Box1 : 5.000000 2013-09-22 21:25:33.316 ClassAndObjects[387:303] Volume of Box2 : 10.000000
Properties:
Properties are introduced in Objective-C to ensure that the instance variable of the class can be accessed outside the class.
The various parts are the property declaration are as follows.- Properties begin with @property, which is a keyword
- It is followed with access specifiers, which are nonatomic or atomic, readwrite or readonly and strong, unsafe_unretained or weak. This varies based on the type of the variable. For any pointer type, we can use strong, unsafe_unretained or weak. Similarly for other types we can use readwrite or readonly.
- This is followed by the datatype of the variable.
- Finally, we have the property name terminated by a semicolon.
- We can add synthesize statement in the implementation class. But in the latest XCode, the synthesis part is taken care by the XCode and you need not include synthesize statement.
It is only possible with the properties we can access the instance variables of the class. Actually, internally getter and setter methods are created for the properties.
For example, let's assume we have a property @property (nonatomic ,readonly ) BOOL isDone. Under the hood, there are setters and getters created as shown below.
-(void)setIsDone(BOOL)isDone; -(BOOL)isDone;
*****************************************************************************
One of the most important concepts in object-oriented programming is that of inheritance. Inheritance allows us to define a class in terms of another class which makes it easier to create and maintain an application. This also provides an opportunity to reuse the code functionality and fast implementation time.
When creating a class, instead of writing completely new data members and member functions, the programmer can designate that the new class should inherit the members of an existing class. This existing class is called the base class, and the new class is referred to as the derived class.
The idea of inheritance implements the is a relationship. For example, mammal IS-A animal, dog IS-A mammal, hence dog IS-A animal as well and so on.
Base & Derived Classes:
Objective-C allows only multilevel inheritance, i.e., it can have only one base class but allows multilevel inheritance. All classes in Objective-C is derived from the superclass NSObject.
@interface derived-class: base-class
Consider a base class Person and its derived class Employee as follows:
#import <Foundation/Foundation.h> @interface Person : NSObject { NSString *personName; NSInteger personAge; } - (id)initWithName:(NSString *)name andAge:(NSInteger)age; - (void)print; @end @implementation Person - (id)initWithName:(NSString *)name andAge:(NSInteger)age{ personName = name; personAge = age; return self; } - (void)print{ NSLog(@"Name: %@", personName); NSLog(@"Age: %ld", personAge); } @end @interface Employee : Person { NSString *employeeEducation; } - (id)initWithName:(NSString *)name andAge:(NSInteger)age andEducation:(NSString *)education; - (void)print; @end @implementation Employee - (id)initWithName:(NSString *)name andAge:(NSInteger)age andEducation: (NSString *)education { personName = name; personAge = age; employeeEducation = education; return self; } - (void)print { NSLog(@"Name: %@", personName); NSLog(@"Age: %ld", personAge); NSLog(@"Education: %@", employeeEducation); } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSLog(@"Base class Person Object"); Person *person = [[Person alloc]initWithName:@"Raj" andAge:5]; [person print]; NSLog(@"Inherited Class Employee Object"); Employee *employee = [[Employee alloc]initWithName:@"Raj" andAge:5 andEducation:@"MBA"]; [employee print]; [pool drain]; return 0; }
When the above code is compiled and executed, it produces the following result:
2013-09-22 21:20:09.842 Inheritance[349:303] Base class Person Object 2013-09-22 21:20:09.844 Inheritance[349:303] Name: Raj 2013-09-22 21:20:09.844 Inheritance[349:303] Age: 5 2013-09-22 21:20:09.845 Inheritance[349:303] Inherited Class Employee Object 2013-09-22 21:20:09.845 Inheritance[349:303] Name: Raj 2013-09-22 21:20:09.846 Inheritance[349:303] Age: 5 2013-09-22 21:20:09.846 Inheritance[349:303] Education: MBA
Access Control and Inheritance:
A derived class can access all the private members of its base class if it's defined in the interface class, but it cannot access private members that are defined in the implementation file.
We can summarize the different access types according to who can access them in the following way:
A derived class inherits all base class methods and variables with the following exceptions:
- Variables declared in implementation file with the help of extensions is not accessible.
- Methods declared in implementation file with the help of extensions is not accessible.
- In case the inherited class implements the method in base class, then the method in derived class is executed.
*******************************************************************
The word polymorphism means having many forms. Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance.
Objective-C polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
Consider the example, we have a class Shape that provides the basic interface for all the shapes. Square and Rectangle are derived from the base class Shape.
We have the method printArea that is going to show about the OOP feature polymorphism.
#import <Foundation/Foundation.h> @interface Shape : NSObject { CGFloat area; } - (void)printArea; - (void)calculateArea; @end @implementation Shape - (void)printArea{ NSLog(@"The area is %f", area); } - (void)calculateArea{ } @end @interface Square : Shape { CGFloat length; } - (id)initWithSide:(CGFloat)side; - (void)calculateArea; @end @implementation Square - (id)initWithSide:(CGFloat)side{ length = side; return self; } - (void)calculateArea{ area = length * length; } - (void)printArea{ NSLog(@"The area of square is %f", area); } @end @interface Rectangle : Shape { CGFloat length; CGFloat breadth; } - (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth; @end @implementation Rectangle - (id)initWithLength:(CGFloat)rLength andBreadth:(CGFloat)rBreadth{ length = rLength; breadth = rBreadth; return self; } - (void)calculateArea{ area = length * breadth; } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Shape *square = [[Square alloc]initWithSide:10.0]; [square calculateArea]; [square printArea]; Shape *rect = [[Rectangle alloc] initWithLength:10.0 andBreadth:5.0]; [rect calculateArea]; [rect printArea]; [pool drain]; return 0; }
When the above code is compiled and executed, it produces the following result:
2013-09-22 21:21:50.785 Polymorphism[358:303] The area of square is 100.000000 2013-09-22 21:21:50.786 Polymorphism[358:303] The area is 50.000000
In the above example based on the availability of the method calculateArea and printArea, either the method in the base class or the derived class executed.
Polymorphism handles the switching of methods between the base class and derived class based on the method implementation of the two classes.
********************************************************************************
All Objective-C programs are composed of the following two fundamental elements:
- Program statements (code): This is the part of a program that performs actions and they are called methods.
- Program data: The data is the information of the program which is affected by the program functions.
Encapsulation is an Object-Oriented Programming concept that binds together the data and functions that manipulate the data and that keeps both safe from outside interference and misuse. Data encapsulation led to the important OOP concept of data hiding.
Data encapsulation is a mechanism of bundling the data and the functions that use them, and data abstraction is a mechanism of exposing only the interfaces and hiding the implementation details from the user.
Objective-C supports the properties of encapsulation and data hiding through the creation of user-defined types, called classes. For example:
@interface Adder : NSObject { NSInteger total; } - (id)initWithInitialNumber:(NSInteger)initialNumber; - (void)addNumber:(NSInteger)newNumber; - (NSInteger)getTotal; @end
The variable total is private and we cannot access from outside the class. This means that they can be accessed only by other members of the Adder class and not by any other part of your program. This is one way encapsulation is achieved.
Methods inside the interface file are accessible and are public in scope.
There are private methods, which are written with the help of extensions, which we will learn in upcoming chapters.
Data Encapsulation Example:
Any Objective-C program where you implement a class with public and private members variables is an example of data encapsulation and data abstraction. Consider the following example:
#import <Foundation/Foundation.h> @interface Adder : NSObject { NSInteger total; } - (id)initWithInitialNumber:(NSInteger)initialNumber; - (void)addNumber:(NSInteger)newNumber; - (NSInteger)getTotal; @end @implementation Adder -(id)initWithInitialNumber:(NSInteger)initialNumber{ total = initialNumber; return self; } - (void)addNumber:(NSInteger)newNumber{ total = total + newNumber; } - (NSInteger)getTotal{ return total; } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Adder *adder = [[Adder alloc]initWithInitialNumber:10]; [adder addNumber:5]; [adder addNumber:4]; NSLog(@"The total is %ld",[adder getTotal]); [pool drain]; return 0; }
When the above code is compiled and executed, it produces the following result:
2013-09-22 21:17:30.485 DataEncapsulation[317:303] The total is 19
Above class adds numbers together and returns the sum. The public members addNum and getTotal are the interfaces to the outside world and a user needs to know them to use the class. The private member total is something that is hidden from the outside world, but is needed for the class to operate properly.
Designing Strategy:
Most of us have learned through bitter experience to make class members private by default unless we really need to expose them. That's just good encapsulation.
It's important to understand data encapsulation since it's one of the core features of all Object-Oriented Programming (OOP) languages including Objective-C.
****************************************************************
Sometimes, you may find that you wish to extend an existing class by adding behavior that is useful only in certain situations. In order add such extension to existing classes, Objective-C provides categories and extensions.
If you need to add a method to an existing class, perhaps, to add functionality to make it easier to do something in your own application, the easiest way is to use a category.
The syntax to declare a category uses the @interface keyword, just like a standard Objective-C class description, but does not indicate any inheritance from a subclass. Instead, it specifies the name of the category in parentheses, like this:
@interface ClassName (CategoryName) @end
Characteristics of category
- A category can be declared for any class, even if you don't have the original implementation source code.
- Any methods that you declare in a category will be available to all instances of the original class, as well as any subclasses of the original class.
- At runtime, there's no difference between a method added by a category and one that is implemented by the original class.
Now, let's look at a sample category implementation. Let's add a category to the Cocoa class NSString. This category will make it possible for us to add a new method getCopyRightString which helps us in returning the copyright string. It is shown below.
#import <Foundation/Foundation.h> @interface NSString(MyAdditions) +(NSString *)getCopyRightString; @end @implementation NSString(MyAdditions) +(NSString *)getCopyRightString{ return @"Copyright TutorialsPoint.com 2013"; } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSString *copyrightString = [NSString getCopyRightString]; NSLog(@"Accessing Category: %@",copyrightString); [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-22 21:19:12.125 Categories[340:303] Accessing Category: Copyright TutorialsPoint.com 2013
Even though any methods added by a category are available to all instances of the class and its subclasses, you'll need to import the category header file in any source code file where you wish to use the additional methods, otherwise you'll run into compiler warnings and errors.
In our example, since we just have a single class, we have not included any header files, in such a case we should include the header files as said above.
*******************************************************************************
Before starting about Posing in Objective-C, I would like to bring to your notice that Posing was declared deprecated in Mac OS X 10.5 and it's not available for use thereafter. So for those who are not concerned about these deprecated methods can skip this chapter.
Objective-C permits a class to wholly replace another class within a program. The replacing class is said to "pose as" the target class.
For the versions that supported posing, all messages sent to the target class are instead received by the posing class.
NSObject contains the poseAsClass: method that enables us to replace the existing class as said above.
Restrictions in Posing
- A class may only pose as one of its direct or indirect superclasses.
- The posing class must not define any new instance variables that are absent from the target class (though it may define or override methods).
- The target class may not have received any messages prior to the posing.
- A posing class can call overridden methods through super, thus incorporating the implementation of the target class.
- A posing class can override methods defined in categories.
#import <Foundation/Foundation.h> @interface MyString : NSString @end @implementation MyString - (NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement { NSLog(@"The Target string is %@",target); NSLog(@"The Replacement string is %@",replacement); } @end int main() { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [MyString poseAsClass:[NSString class]]; NSString *string = @"Test"; [string stringByReplacingOccurrencesOfString:@"a" withString:@"c"]; [pool drain]; return 0; }
Now when we compile and run the program in a older Mac OS X (V_10.5 or earlier), we will get the following result.
2013-09-22 21:23:46.829 Posing[372:303] The Target string is a 2013-09-22 21:23:46.830 Posing[372:303] The Replacement string is c
In the above example, we just polluted the original method with our implementation and this will get affected throughout all the NSString operations with the above method.
******************************************************************************
A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension).
The methods declared by a class extension are implemented in the implementation block for the original class, so you can't, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString..
Extensions are actually categories without the category name. It's often referred as anonymous categories.
The syntax to declare a extension uses the @interface keyword, just like a standard Objective-C class description, but does not indicate any inheritance from a subclass. Instead, it just adds parentheses, as shown below
@interface ClassName () @end
Characteristics of extensions
- An extension cannot be declared for any class, only for the classes that we have original implementation of source code.
- An extension is adding private methods and private variables that are only specific to the class.
- Any method or variable declared inside the extensions is not accessible even to the inherited classes.
Extensions Example
Let's create a class SampleClass that has an extension. In the extension, let's have a private variable internalID.
Then, let's have a method getExternalID that returns the externalID after processing the internalID.
The example is shown below and this wont work on online compiler.
#import <Foundation/Foundation.h> @interface SampleClass : NSObject { NSString *name; } - (void)setInternalID; - (NSString *)getExternalID; @end @interface SampleClass() { NSString *internalID; } @end @implementation SampleClass - (void)setInternalID{ internalID = [NSString stringWithFormat: @"UNIQUEINTERNALKEY%dUNIQUEINTERNALKEY",arc4random()%100]; } - (NSString *)getExternalID{ return [internalID stringByReplacingOccurrencesOfString: @"UNIQUEINTERNALKEY" withString:@""]; } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass setInternalID]; NSLog(@"ExternalID: %@",[sampleClass getExternalID]); [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-22 21:18:31.754 Extensions[331:303] ExternalID: 51
In the above example, we can see that the internalID is not returned directly. We here remove the UNIQUEINTERNALKEY and only make the remaining value available to the method getExternalID.
The above example just uses a string operation, but it can have many features like encryption/decryption and so on.
******************************************************************
Objective-C allows you to define protocols, which declare the methods expected to be used for a particular situation. Protocols are implemented in the classes conforming to the protocol.
A simple example would be a network URL handling class, it will have a protocol with methods like processCompleted delegate method that intimates the calling class once the network URL fetching operation is over.
A syntax of protocol is shown below.
@protocol ProtocolName @required // list of required methods @optional // list of optional methods @end
The methods under keyword @required must be implemented in the classes that conforms to the protocol and the methods under @optional keyword are optional to implement.
Here is the syntax for class conforming to protocol
@interface MyClass : NSObject <MyProtocol> ... @end
This means that any instance of MyClass will respond not only to the methods declared specifically in the interface, but that MyClass also provides implementations for the required methods in MyProtocol. There's no need to redeclare the protocol methods in the class interface - the adoption of the protocol is sufficient.
If you need a class to adopt multiple protocols, you can specify them as a comma-separated list. We have a delegate object that holds the reference of the calling object that implements the protocol.
An example is shown below.
#import <Foundation/Foundation.h> @protocol PrintProtocolDelegate - (void)processCompleted; @end @interface PrintClass :NSObject { id delegate; } - (void) printDetails; - (void) setDelegate:(id)newDelegate; @end @implementation PrintClass - (void)printDetails{ NSLog(@"Printing Details"); [delegate processCompleted]; } - (void) setDelegate:(id)newDelegate{ delegate = newDelegate; } @end @interface SampleClass:NSObject<PrintProtocolDelegate> - (void)startAction; @end @implementation SampleClass - (void)startAction{ PrintClass *printClass = [[PrintClass alloc]init]; [printClass setDelegate:self]; [printClass printDetails]; } -(void)processCompleted{ NSLog(@"Printing Process Completed"); } @end int main(int argc, const char * argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass startAction]; [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-22 21:15:50.362 Protocols[275:303] Printing Details 2013-09-22 21:15:50.364 Protocols[275:303] Printing Process Completed
In the above example we have seen how the delgate methods are called and executed. Its starts with startAction, once the process is completed, the delegate method processCompleted is called to intimate the operation is completed.
In any iOS or Mac app, we will never have a program implemented without a delegate. So its important we understand the usage of delegates. Delegates objects should use unsafe_unretained property type to avoid memory leaks.
*******************************************************************
Dynamic binding is determining the method to invoke at runtime instead of at compile time. Dynamic binding is also referred to as late binding.
In Objective-C, all methods are resolved dynamically at runtime. The exact code executed is determined by both the method name (the selector) and the receiving object.
Dynamic binding enables polymorphism. For example, consider a collection of objects including Rectangle and Square. Each object has its own implementation of a printArea method.
In the following code fragment, the actual code that should be executed by the expression [anObject printArea] is determined at runtime. The runtime system uses the selector for the method run to identify the appropriate method in whatever class of anObject turns out to be.
Let us look at a simple code that would explain dynamic binding.
#import <Foundation/Foundation.h> @interface Square:NSObject { float area; } - (void)calculateAreaOfSide:(CGFloat)side; - (void)printArea; @end @implementation Square - (void)calculateAreaOfSide:(CGFloat)side { area = side * side; } - (void)printArea { NSLog(@"The area of square is %f",area); } @end @interface Rectangle:NSObject { float area; } - (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth; - (void)printArea; @end @implementation Rectangle - (void)calculateAreaOfLength:(CGFloat)length andBreadth:(CGFloat)breadth { area = length * breadth; } - (void)printArea { NSLog(@"The area of Rectangle is %f",area); } @end int main() { Square *square = [[Square alloc]init]; [square calculateAreaOfSide:10.0]; Rectangle *rectangle = [[Rectangle alloc]init]; [rectangle calculateAreaOfLength:10.0 andBreadth:5.0]; NSArray *shapes = [[NSArray alloc]initWithObjects: square, rectangle,nil]; id object1 = [shapes objectAtIndex:0]; [object1 printArea]; id object2 = [shapes objectAtIndex:1]; [object2 printArea]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-28 07:42:29.821 demo[4916] The area of square is 100.000000 2013-09-28 07:42:29.821 demo[4916] The area of Rectangle is 50.000000
As you can see in the above example, printArea method is dynamically selected in runtime. It is an example for dynamic binding and is quite useful in many situations when dealing with similar kind of objects.
*******************************************************************************
We can create subclass within a class cluster that defines a class that embeds within it an object. These class objects are composite objects.
So you might be wondering what's a class cluster. So we will first see what's a class cluster.
Class Clusters
Class clusters are a design pattern that the Foundation framework makes extensive use of. Class clusters group a number of private concrete subclasses under a public abstract superclass. The grouping of classes in this way simplifies the publicly visible architecture of an object-oriented framework without reducing its functional richness. Class clusters are based on the Abstract Factory design pattern.
To make it simple, instead of creating multiple classes for similar functions, we create a single class that will take care of its handling based on the value of input.
For example, in NSNumber we have many clusters of classes like char, int, bool and so on. We group all of them to a single class that takes care of handling the similar operations in a single class. NSNumber actually wraps the value of these primitive types into objects.
So what's exactly composite object?
By embedding a private cluster object in an object of our own design, we create a composite object. This composite object can rely on the cluster object for its basic functionality, only intercepting messages that the composite object wants to handle in some particular way. This architecture reduces the amount of code we must write and lets you take advantage of the tested code provided by the Foundation Framework.
This is explained in the following figure.

The composite object must declare itself to be a subclass of the cluster's abstract superclass. As a subclass, it must override the superclass' primitive methods. It can also override derived methods, but this isn't necessary because the derived methods work through the primitive ones.
The count method of the NSArray class is an example; the intervening object's implementation of a method it overrides can be as simple as:
- (unsigned)count { return [embeddedObject count]; }
In the above example, embedded object is actually of type NSArray.
A Composite Object example
Now in order to see a complete example, let's look at the example from the Apple documentation which is given below.
#import <Foundation/Foundation.h> @interface ValidatingArray : NSMutableArray { NSMutableArray *embeddedArray; } + validatingArray; - init; - (unsigned)count; - objectAtIndex:(unsigned)index; - (void)addObject:object; - (void)replaceObjectAtIndex:(unsigned)index withObject:object; - (void)removeLastObject; - (void)insertObject:object atIndex:(unsigned)index; - (void)removeObjectAtIndex:(unsigned)index; @end @implementation ValidatingArray - init { self = [super init]; if (self) { embeddedArray = [[NSMutableArray allocWithZone:[self zone]] init]; } return self; } + validatingArray { return [[self alloc] init] ; } - (unsigned)count { return [embeddedArray count]; } - objectAtIndex:(unsigned)index { return [embeddedArray objectAtIndex:index]; } - (void)addObject:(id)object { if (object != nil) { [embeddedArray addObject:object]; } } - (void)replaceObjectAtIndex:(unsigned)index withObject:(id)object; { if (index <[embeddedArray count] && object != nil) { [embeddedArray replaceObjectAtIndex:index withObject:object]; } } - (void)removeLastObject; { if ([embeddedArray count] > 0) { [embeddedArray removeLastObject]; } } - (void)insertObject:(id)object atIndex:(unsigned)index; { if (object != nil) { [embeddedArray insertObject:object atIndex:index]; } } - (void)removeObjectAtIndex:(unsigned)index; { if (index <[embeddedArray count]) { [embeddedArray removeObjectAtIndex:index]; } } @end int main() { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ValidatingArray *validatingArray = [ValidatingArray validatingArray]; [validatingArray addObject:@"Object1"]; [validatingArray addObject:@"Object2"]; [validatingArray addObject:[NSNull null]]; [validatingArray removeObjectAtIndex:2]; NSString *aString = [validatingArray objectAtIndex:1]; NSLog(@"The value at Index 1 is %@",aString); [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-28 22:03:54.294 demo[6247] The value at Index 1 is Object2
In the above example, we can see that validating array's one function would not allow adding null objects that will lead to crash in the normal scenario. But our validating array takes care of it. Similarly, each of the method in validating array adds validating processes apart from the normal sequence of operations.
*****************************************************************
If you refer Apple documentation, you can see the details of Foundation framework as given below.
The Foundation framework defines a base layer of Objective-C classes. In addition to providing a set of useful primitive object classes, it introduces several paradigms that define functionality not covered by the Objective-C language. The Foundation framework is designed with these goals in mind:
- Provide a small set of basic utility classes.
- Make software development easier by introducing consistent conventions for things such as deallocation.
- Support Unicode strings, object persistence, and object distribution.
- Provide a level of OS independence to enhance portability.
The framework was developed by NeXTStep, which was acquired by Apple and these foundation classes became part of Mac OS X and iOS.
Since it was developed by NeXTStep, it has class prefix of "NS".
We have used Foundation Framework in all our sample programs. It is almost a must to use Foundation Framework.
Generally, we use something like #import <Foundation/NSString.h> to import a Objective-C class, but in order avoid importing too many classes, it's all imported in #import <Foundation/Foundation.h>.
NSObject is the base class of all objects including the foundation kit classes. It provides the methods for memory management. It also provides basic interface to the runtime system and ability to behave as Objective-C objects. It doesn't have any base class and is the root for all classes.
Foundation Classes based on functionality
Loop Type | Description |
---|---|
Data storage | NSArray, NSDictionary, and NSSet provide storage for Objective-C objects of any class. |
Text and strings | NSCharacterSet represents various groupings of characters that are used by the NSString and NSScanner classes. The NSString classes represent text strings and provide methods for searching, combining, and comparing strings. An NSScanner object is used to scan numbers and words from an NSString object. |
Dates and times | The NSDate, NSTimeZone, and NSCalendar classes store times and dates and represent calendrical information. They offer methods for calculating date and time differences. Together with NSLocale, they provide methods for displaying dates and times in many formats and for adjusting times and dates based on location in the world. |
Exception handling | Exception handling is used to handle unexpected situations and it's offered in Objective-C with NSException. |
File handling | File handling is done with the help of class NSFileManager. |
URL loading system | A set of classes and protocols that provide access to common Internet protocols. |
********************************************************************************
Fast enumeration is an Objective-C's feature that helps in enumerating through a collection. So in order to know about fast enumeration, we need know about collection first which will be explained in the following section.
Collections in Objective-C
Collections are fundamental constructs. It is used to hold and manage other objects. The whole purpose of a collection is that it provides a common way to store and retrieve objects efficiently.
There are several different types of collections. While they all fulfil the same purpose of being able to hold other objects, they differ mostly in the way objects are retrieved. The most common collections used in Objective-C are:
- NSSet
- NSArray
- NSDictionary
- NSMutableSet
- NSMutableArray
- NSMutableDictionary
If you want to know more about these structures, please refer data storage in Foundation Framework.
Fast enumeration Syntax
for (classType variable in collectionObject ) { statements }
Here is an example for fast enumeration.
#import <Foundation/Foundation.h> int main() { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray *array = [[NSArray alloc] initWithObjects:@"string1", @"string2",@"string3",nil]; for(NSString *aString in array) { NSLog(@"Value: %@",aString); } [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-28 06:26:22.835 demo[7426] Value: string1 2013-09-28 06:26:22.836 demo[7426] Value: string2 2013-09-28 06:26:22.836 demo[7426] Value: string3
As you can see in the output, each of the objects in the array is printed in an order.
Fast Enumeration Backwards
for (classType variable in [collectionObject reverseObjectEnumerator] ) { statements }
Here is an example for reverseObjectEnumerator in fast enumeration.
#import <Foundation/Foundation.h> int main() { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; NSArray *array = [[NSArray alloc] initWithObjects:@"string1", @"string2",@"string3",nil]; for(NSString *aString in [array reverseObjectEnumerator]) { NSLog(@"Value: %@",aString); } [pool drain]; return 0; }
Now when we compile and run the program, we will get the following result.
2013-09-28 06:27:51.025 demo[12742] Value: string3 2013-09-28 06:27:51.025 demo[12742] Value: string2 2013-09-28 06:27:51.025 demo[12742] Value: string1
As you can see in the output, each of the objects in the array is printed but in the reverse order as compared to normal fast enumeration.
***************************************************************
Memory management is one of the most important process in any programming language. It is the process by which the memory of objects are allocated when they are required and deallocated when they are no longer required.
Managing object memory is a matter of performance; if an application doesn't free unneeded objects, its memory footprint grows and performance suffers.
Objective-C Memory management techniques can be broadly classified into two types.
- "Manual Retain-Release" or MRR
- "Automatic Reference Counting" or ARC
"Manual Retain-Release" or MRR
In MRR, we explicitly manage memory by keeping track of objects on our own. This is implemented using a model, known as reference counting, that the Foundation class NSObject provides in conjunction with the runtime environment.
The only difference between MRR and ARC is that the retain and release is handled by us manually in former while its automatically taken care of in the latter.
The following figure represents an example of how memory management work in Objective-C.

The memory life cycle of the Class A object is shown in the above figure. As you can see, the retain count is shown below the object, when the retain count of an object becomes 0, the object is freed completely and its memory is deallocated for other objects to use.
Class A object is first created using alloc/init method available in NSObject. Now, the retain count becomes 1.
Now, class B retains the Class A's Object and the retain count of Class A's object becomes 2.
Then, Class C makes a copy of the object. Now, it is created as another instance of Class A with same values for the instance variables. Here, the retain count is 1 and not the retain count of the original object. This is represented by the dotted line in the figure.
The copied object is released by Class C using the release method and the retain count becomes 0 and hence the object is destroyed.
In case of the initial Class A Object, the retain count is 2 and it has to be released twice in order for it to be destroyed. This is done by release statements of Class A and Class B which decrements the retain count to 1 and 0, respectively. Finally, the object is destroyed.
MRR Basic Rules
- We own any object we create: We create an object using a method whose name begins with "alloc", "new", "copy", or "mutableCopy"
- We can take ownership of an object using retain: A received object is normally guaranteed to remain valid within the method it was received in, and that method may also safely return the object to its invoker. We use retain in two situations:
- In the implementation of an accessor method or an init method, to take ownership of an object we want to store as a property value.
- To prevent an object from being invalidated as a side-effect of some other operation.
- When we no longer need it, we must relinquish ownership of an object we own: We relinquish ownership of an object by sending it a release message or an autorelease message. In Cocoa terminology, relinquishing ownership of an object is therefore typically referred to as "releasing" an object.
- You must not relinquish ownership of an object you do not own: This is just corollary of the previous policy rules stated explicitly.
#import <Foundation/Foundation.h> @interface SampleClass:NSObject - (void)sampleMethod; @end @implementation SampleClass - (void)sampleMethod { NSLog(@"Hello, World! \n"); } - (void)dealloc { NSLog(@"Object deallocated"); [super dealloc]; } @end int main() { /* my first program in Objective-C */ SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass sampleMethod]; NSLog(@"Retain Count after initial allocation: %d", [sampleClass retainCount]); [sampleClass retain]; NSLog(@"Retain Count after retain: %d", [sampleClass retainCount]); [sampleClass release]; NSLog(@"Retain Count after release: %d", [sampleClass retainCount]); [sampleClass release]; NSLog(@"SampleClass dealloc will be called before this"); // Should set the object to nil sampleClass = nil; return 0; }
When we compile the above program, we will get the following output.
2013-09-28 04:39:52.310 demo[8385] Hello, World! 2013-09-28 04:39:52.311 demo[8385] Retain Count after initial allocation: 1 2013-09-28 04:39:52.311 demo[8385] Retain Count after retain: 2 2013-09-28 04:39:52.311 demo[8385] Retain Count after release: 1 2013-09-28 04:39:52.311 demo[8385] Object deallocated 2013-09-28 04:39:52.311 demo[8385] SampleClass dealloc will be called before this
"Automatic Reference Counting" or ARC
In Automatic Reference Counting or ARC, the system uses the same reference counting system as MRR, but it inserts the appropriate memory management method calls for us at compile-time. We are strongly encouraged to use ARC for new projects. If we use ARC, there is typically no need to understand the underlying implementation described in this document, although it may in some situations be helpful. For more about ARC, see Transitioning to ARC Release Notes.
As mentioned above, in ARC, we need not add release and retain methods since that will be taken care by the compiler. Actually, the underlying process of Objective-C is still the same. It uses the retain and release operations internally making it easier for the developer to code without worrying about these operations, which will reduce both the amount of code written and the possibility of memory leaks.
There was another principle called garbage collection, which is used in Mac OS-X along with MRR, but since its deprecation in OS-X Mountain Lion, it has not been discussed along with MRR. Also, iOS objects never had garbage collection feature. And with ARC, there is no use of garbage collection in OS-X too.
Here is a simple ARC example. Note this won't work on online compiler since it does not support ARC.
#import <Foundation/Foundation.h> @interface SampleClass:NSObject - (void)sampleMethod; @end @implementation SampleClass - (void)sampleMethod { NSLog(@"Hello, World! \n"); } - (void)dealloc { NSLog(@"Object deallocated"); } @end int main() { /* my first program in Objective-C */ @autoreleasepool{ SampleClass *sampleClass = [[SampleClass alloc]init]; [sampleClass sampleMethod]; sampleClass = nil; } return 0; }
When we compile the above program, we will get the following output.
2013-09-28 04:45:47.310 demo[8385] Hello, World! 2013-09-28 04:45:47.311 demo[8385] Object deallocated
No comments:
Post a Comment