Objects, Pointers and Primitives in Objective-C

Objective-C adds lots of things to C — you have C constructs like structs, unions and pointers, but also objects, protocols, reference counting and garbage collection and a lot more.

An object and its primitive counterpart

I came across using classes like NSNumber and method declarations using a primitive like int or long. A variable “holding an” or referring to an object is actually a pointer, the type id is a generic pointer type.

Imagine the following prototype:

- (void) fun3: (long) j;

And a call to it:

// Create NSNumber object from integer value
NSNumber* num = [NSNumber numberWithInt: 123];
// Call fun3 with NSNumber object as parameter
[self fun3: num];

Does this work? Whats the result?

In Java there’s auto-boxing and auto-unboxing: auto-boxing is the process of automatically wrapping a primitive int variable or literal in an java.lang.Integer object and auto-unboxing is vice versa. So the following is ok:

public void autobox() {
    Integer i = 1; // 1 gets wrapped as new Integer(1)
    int j = i;     // j will contain 1; same as i.intValue()
}
public void autobox(int i) {
    int j = i; // Will contain value of i; same as i.intValue()
}
autobox(new Integer(23));

There’s not auto-boxing in Objective-C 2.0. So what will happen with the code above? It will compile and run! What happens is that (long) j will contain a pointer to (a copy of the original pointer num) the same NSNumber object at num.

- (void) fun3: (long) j {
    // Prints j = 1199616, the address-of the copied(!) parameter
    NSLog(@"j = %u", j);
    // Cast object at pointer j to NSNumber
    NSNumber* n = (NSNumber*) j;
    // Prints 123
    NSLog(@"n intValue = %i", [n intValue]);
    // Query the type of what j points to: NSCFNumber
    NSLog(@"class of (id) j = %@", [((id) j) class]);
    NSLog(@"%i", [((id) j) intValue]); // Will print 123, too
    NSLog(@"%i", ((id) j)); // Will print 1199616, too
}

So a more correct variant is to write parameter j as (long*) j as it’s a pointer.

Using int instead of long as parameter type in fun3 above will result in a compiler warning

warning: cast to pointer from integer of different size

as pointers are 64 bits wide (sizeof(j) == 8 * 8 = 64, on my MacBook x86_64 system) and int is 32 bits (sizeof(int) = 4 * 8 = 32).

The solution is to be sure what types are used and write the function as:

- (void) fun3: (NSNumber*) n {
    // Prints 123
    NSLog(@"n intValue = %i", [n intValue]);
}

Releasing an object: dangling pointers

When an object is released either through a message like release or through garbage collection a call to fun3 will still succeed! But: the “valid” pointer will refer to an invalid/unusable object or memory location!

// Create NSNumber object from integer value
NSNumber* num = [NSNumber numberWithInt: 123];
// Release object
[num release];
// Call fun3 with pointer to invalid NSNumber object
[self fun3: num];

// BOOM: in fun3 the pointer refers to an invalid object
- (void) fun3: (long) j {
    // Prints j = 1199616, the address-of the copied(!) parameter
    NSLog(@"j = %u", j);
    // BOOM: the following will fail as the pointer refers to released object
    NSNumber* n = (NSNumber*) j; // Cast object at pointer j to NSNumber
}

This will produce a EXC_BAD_ACCESS signal within fun3 and your program may crash.

Take care!

Resources

Ralf Bensmann

Ralf Bensmann

Software Architect, Trainer, Author
Java Standard and Enterprise Edition
Clojure, Groovy & Grails
OpenOffice, LibreOffice

Archive

2012 (3)
2011 (43)
2010 (34)
Posterous theme by Cory Watilo