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!
