What is NSObject ?

 

Many object oriented programming languages, have a class, from which all other classes extends.

In general, a class has a set of methods, and variables, which it defines. The variables being the data, and the methods, being the operations, that can be performed over the data.

Data and methods, can belong to the class itself, so they are applied on the class level, or they can belong, to what is called, an instance of the class.

A class can have multiple instances created from it, each having its own data values, and each capable of using the methods, that the class has defined for it. Additionally an instance can call, by referring to the class, the class methods, and access its data, if the class allows that.

This being said, if a class extends another class, the data and methods, which are defined on the class that is being extended, become part, of the extending class. So the extending class, will have the other class, class methods and data, and the methods and data, which the extended class, defines for its instances, will also be defined for the instances of the extending class.

Java has for every top level class, a class, that does not have a parent, the Objectclass as its parent, whereas for C++, this is not the case, but you can explicitly create a set of classes, and their descendants, having a given class, that you define, as their ancestor class.

For objective-C, not each class, is made to be implicitly descendant, of a base class, as in java , as an example, a class, which does not have any parent:

// NoRoot.h

#import <Foundation/Foundation.h>
@interface NoRoot
+ (void ) initialize;
+ (void ) print;
@end


// NoRoot.m
#import "NoRoot.h"

static int x = 1 ;
static int y = 2 ;

@implementation NoRoot

+ (void )initialize {}
/* The initialize method, is called
 by the runtime, before any method
 of the class, or of one of its 
 instances is called .
 This is done only once, for each
 class .*/

+ (void ) print{
	printf ("NoRoot x is : %d\n" , x );
	printf ("NoRoot y is : %d\n" , y );}
@end

// main.m

#import "NoRoot.h"
int main (int argc, const char* argv[ ] ){
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc ] init ];
    [NoRoot print ];
    [pool release ];
    return 0; }

/*Output:
NoRoot x is : 1
NoRoot y is : 2 .*/

This being said, in objective-C, there are some classes, which do not have any parents, and from which many other classes derive, these classes are called root classes. An example of a root class, is the NSObject class, of which all the classes of cocoa, and cocoa touch frameworks, inherits.

Having a root class, allow certain operations, to be performed, over all the objects, as in checking for their equality, or allocating memory.

This being said, one must ask, what kind of operations, the NSObject class allows, or opens up for grab?

Well first of all, NSObject , defines methods related to memory management. So you have the alloc method, which allows memory to be allocated for an object, the init method, which allows an object memory, to be initialized to certain values, and the new method, which is basically a call to alloc , followed by a call to init, so it is as if, doing both operations, using one function.

@interface NSObject ...
+ (id) alloc;
/* Class method to allocate memory .*/

- (id) init;
/* instance method to initialize memory .*/

+ (id) new;
/* Basically a combination of a call
   to alloc, and a call to init .*/ 

- (id) copy;
/* Copy method, allows copying
   an object .*/
...
@end 

Some methods implemented by NSObject, are actually just stubs, so they provide a mock implementation, of an operation, as in, the copy method, implemented by NSObject, just returns self, so it does not do any copying, and hence the idea, that it is up to the developer, to provide the actual implementation of the method, depending on the kind of data.

Having allocated memory, memory must be freed. In C, this can be done, by calling the free method, so in other words, it is up to you, to do it. In java there is garbage collection, so you do not need to worry, about freeing memory .

In objective-C, for a short period, there was also garbage collection, as in java, but this was deprecated, and removed starting macOS sierra.

Still, in objective-C, you are not the one , who is responsible for directly making the call, to free up memory, but what is used instead, is reference counting.

Reference counting, is basically counting the number of references, to an object, and if the number of references reaches zero , the object reserved memory is freed.

This was originally done, using a manual reference counting scheme, or algorithm, so it was you, as a developer, who was responsible, for incrementing and decrementing, the count of references to an object.

How is this going to happen, you might ask!? well simply, the NSObject class, has some methods , which allowed to increment, and decrement, the number of references to an object.

@interface NSObject ...
- (id) retain;
// increment an object reference count.

- (oneway void) release;
// decrement an object reference count.

- (id) autorelease;
/* In simple terms, you are asking
the count decrement, to be managed,
and count decrement will happen, when 
a call to the manager of the count, is 
made .*/

- (NSUInteger) retainCount;
/* Returns the number of reference count
for an object. This method should not 
be used .*/


- (void) dealloc;
/* Once count reaches 0, the dealloc
method is called.
As with some other methods of NSObject, 
this method is called automatically by 
the runtime. 
In other words, the runtime will call
some methods on the class, and on its
instances automatically, so it is like
certain aspects of the code, is being 
managed by the runtime .*/
...
@end

Once the number of references reaches 0, an object dealloc method is called, hence this gives a chance to the object, to decrement the count of other objects which it has retained, in other words to which it has incremented, their reference count.

After that dealloc is called, the object reserved memory is freed, so the system can use it for other purposes.

Automatic reference counting, came up later on, and when it is being used, incrementing and decrementing the count of references, to an object, is done by the compiler, so you should not be calling, retain or release.

The NSObject class, is declared in the NSObject.h header file, and is part of the foundation framework. Additionally the NSObject.h header file, defines the NSObject protocol.

A protocol in objective-C, if you come from the java world, is more or less, a java interface. Basically a protocol, is just a set of functions, that must be defined, by the implementing class.

This being said, the NSObject class, implements the NSObject protocol, and additional methods, so it defines all the methods, declared in the NSObject protocol, in order to have all objects, perform certain kind of operations.

All the methods described earlier, besides copy, are part of the NSObject protocol. This protocol, additionally defines method, for object introspection.

- (Class ) class;
/* returns the Object class .*/

- (Class ) superclass;
/* returns the Object superClass .*/

- (BOOL ) isMemberOfClass :(Class )aClass;
/* returns true, if this object, is a 
direct child of aClass .*/

- (BOOL ) isKindOfClass :(Class )aClass;
/* returns true, if this object, is
a descendant of aClass .*/

- (BOOL ) isProxy;
/* returns true, if the class
is not a descendant, of NSObject .*/

- (BOOL ) respondsToSelector :(SEL )aSelector;
/* returns true, if the object
implements the selected, or 
specified method .*/

- (BOOL ) conformsToProtocol :(Protocol* )aProtocol;
/* returns true, if the object
implements the specified protocol .*/

- (id ) self;
/* Returns a reference to the object */

An example of the previous explained methods, is :

#import <Foundation/Foundation.h>

int main (int argc, const char* argv [ ] ){
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc ] init ];

    //Example class method
    NSObject* nsobject = [[NSObject alloc ] init];
    NSLog ( @"nsobect class is : %@", [nsobject class ]);
    // .. nsobect class is : NSObject


    //Example superclass method
    NSString* ns_string = @"hey!";
    NSLog ( @"ns_string super class is : %@", [ns_string superclass ]);
    // .. ns_string super class is : NSMutableString


    //Example isMemberOfClass
    if ([ns_string isMemberOfClass :[nsobject class ]] == NO )
        NSLog ( @"ns_string member of NSObject? N" );
    // .. ns_string member of NSObject? N


    //Example isKindOfClass
    if ([ns_string isKindOfClass :[nsobject class ]] == YES )
        NSLog ( @"ns_string kind of NSObject? Y" );
    // ..  ns_string kind of NSObject? Y


    //Example isProxy 
    if ([ns_string isProxy ] == NO )
        NSLog ( @"ns_string is proxy ? N" );
    // .. ns_string is proxy ? N


    // respondsToSelector example
    if ([ns_string respondsToSelector :@selector(isMemberOfClass: )])
        NSLog ( @"ns_string has the method isMemberOfClass? Y" );
    // .. ns_string has the method isMemberOfClass? Y


    // conformsToProtocol example
    if ([nsobject conformsToProtocol :@protocol(NSObject )])
        NSLog ( @"nsobject conforms to the NSObject protocol? Y" );
    // .. nsobject conforms to the NSObject protocol? Y


    // self method example
    NSLog ( @"%@" , [nsobject self ] );
    // .. <NSObject: 0x10010c5c0>
    [pool release ];
    return 0; }

Additionally, the NSObject protocol , defines methods, which allow instances to perform, equality, hashing and description.

- (BOOL ) isEqual :(id )anObject;
/* True, if two objects are equal.
The default implementation, checks if
two objects, have the same reference .*/

- (NSUInteger ) hash;
/* If two objects are equal, the hash
method, must return the same integral
value .*/

- (NSString *) description;
/* A String describing an instance .*/

As an example,

#import <Foundation/Foundation.h>

int main (int argc, const char* argv[ ] ){
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc ] init ];

    NSObject* object = [NSObject new ];

    NSLog (@"Object description : %@", object );
    // .. Object description <NSObject: 0x10010c5c0>
    // when using @ , object is printed,
    // as the string returned by descriptionWithLocale
    // if defined, or else by the description 
    // method .

    NSLog (@"Object hash : %lu", [object hash ]);
    // .. Object hash : 1099200

    if ([object isEqual :object ])
        NSLog (@"object == object ? Y" );
    // .. object == object ? Y

    [pool release ];
    return 0; }

Finally, imagine that a class instance, acts as a service provider, like for example, one of its method, do downloading in the background. This method is passed an object, and is asked to call a method, that this object has defined, once it has done its tasks, how should this be possible, you might ask?

Well simply, the NSObject protocol, defines the following methods:

- (id ) performSelector :(SEL )aSelector;
/* call the selected method on the object .*/

- (id ) performSelector :(SEL )aSelector
	      withObject:(id )anObject;
/* Call the selected method on the object,
passing one argument .*/

- (id ) performSelector :(SEL )aSelector
              withObject:(id )object1
              withObject:(id )object2;
/* Call the selected method on the object,
passing two arguments .*/

So once your service has finished performing its tasks, having the passed object, and the selected method which is to be called, it can perform the selected method, on the passed object, using the performSelector method. As an example :

#import <Foundation/Foundation.h>

int main (int argc, const char* argv[ ] ){
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc ] init ];

    NSObject* object_1 = [NSObject new ];

    SEL eq_selector = @selector (isEqual: );

    if ([object_1 respondsToSelector :eq_selector ]) {
        if ([object_1 performSelector :eq_selector withObject:object_1 ] )
            NSLog(@"object_1 == object_2 ? Y" );}
    // .. bject_1 == object_2 ? Y
    [pool release ];
    return 0;}

For a root class, so a class, which does not have any parent, the objective-C runtime, additionally to having methods defined for an instance, be defined on that instance, has these methods, also defined for the class itself, and for its sub classes. So for the NSObject class, methods which are defined for its instances, can also be accessed, or called from the NSObject class itself, or from the classes that extends it.

#import <Foundation/Foundation.h>

@interface Test
+ (void)initialize;
- (void ) print_test;
@end
@implementation Test
+ (void)initialize{}
- (void ) print_test{
    printf("print_test, Instance method\n");}
@end


@interface Test_sub :Test
- (void ) print_test_sub;
@end
@implementation Test_sub
- (void ) print_test_sub{
    printf("print_test_sub, Instance method\n");}
@end


@interface Test_sub_sub :Test_sub
@end
@implementation Test_sub_sub
@end


int main (int argc, const char* argv[ ]){
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc ] init ];

    [Test print_test ];
    //print_test, Instance method

    [Test_sub print_test ];
    //print_test, Instance method

    //[Test_sub print_test_sub ];

    /* The commented code above,
     will cause an error, since
     Test_sub is not a root class,
     it does not have its instance
     methods, defined for it. */

    //[Test_sub_sub print_test_sub ];
    /* Test_sub_sub is not an instance
     of Test_sub, hence the commented
     call above will also cause an 
     error .*/

    [pool release ];
    return 0; }

Finally, as explained earlier on, the NSObject class, defines additional methods , to those required by the NSObject protocol, so for example it defines methods for archiving objects, which is like encoding an object, and storing it, and recreating it afterwards.