BIT-101 [2003-2017]

Memory Allocation for the budding Objective-C programmer


… or, “How I Learned to Stop Worrying and Love malloc”

OK, here’s a scenario:

You have a whole crapload of ints, or floats, or even CGPoints or CGRects. You need to hang onto these things and use them later. Hopefully you are not even considering making a whole crapload of variables to store them in, i.e. myNumber0, myNumber1, myNumber2, etc. If that even crossed your mind, close the browser, turn off the computer, go straight to the book store and pick up the first Programming for Dummies book you can find.

No, of course you immediately thought, “Array!” Good! Now, in Objective-C we have NSArrays and NSMutableArrays. NSArrays have to be initialized with all their content and cannot thereafter change. NSMutableArrays can have things added, removed, and altered at any time. Say you go for the mutable species, and say you want to use it to store a bunch of ints. I guarantee your first attempt would look something like this:

[c]NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:10];
[myArray addObject:0];
[myArray addObject:1];
[myArray addObject:2];
[myArray addObject:3];
[myArray addObject:4];
[myArray addObject:5];
[myArray addObject:6];
[myArray addObject:7];
[myArray addObject:8];
[myArray addObject:9];
[/c]

And as you run your app, it’s going to crash. Hard.

Or say you try to add some points:

[c][myArray addObject:CGPointMake(100, 100)];[/c]

Now you are going to get an error like: “incompatible type for argument 1 of ‘addObject’” Why? because ints are not objects. They are primitive types. In Objective-C, an object is something that extends NSObject.

An int is not an object, but I believe will get interpreted as a pointer. And when it tries to read the “objects” at memory locations 0 through 10, it doesn’t find what it’s looking for.

CGPoints and CGRects are also not objects. They are structs. A struct is essentially a chunk of memory that can hold multiple values of multiple types. A CGPoint holds two floats. In fact, you can see the actual definition by Command-clicking on CGPoint in XCode:

[c]struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;[/c]

(CGFloat is just a typedef for float.)

A struct is not an NSObject so you get an error.

One solution would be to wrap the int in an object. Objective-C has a NSNumber object for this purpose. So you change the code to something like this:

[c][myArray addObject:[NSNumber numberWithInt:0]];
[myArray addObject:[NSNumber numberWithInt:1]];
[myArray addObject:[NSNumber numberWithInt:2]];
//etc.[/c]

Now, when you want to get those ints back, you do something like this:

[c]myInt = [[myArray objectAtIndex:0] intValue];[/c]

I don’t know about you, but that’s an awful lot of overhead for saving and reading a list of integers.

But let’s look at structs. There is no “NSPoint” or “NSRect” so you’d have to create your own class that extends NSObject, wraps up a point or a rectangle, makes it accessible as a public var or a property, and use that in your array. So you get something perhaps like this:

[c][myArray addObject:[MyRectObject objectWithRect:CGRectMake(0, 0, 100, 100)]];[/c]

and similarly wicked verbose code to retrieve the value. And not only is does it take up finger resources to type all that, it takes CPU and memory resources to create all those extra objects, and access them via various methods, not to mention the fact that you’re going through high level “objectAtIndex” methods in the array class itself.

So let’s look at a much simpler way to do it. Enter malloc.

malloc means memory allocation. It’s a very low level, core C function that lets you directly allocate a chunk of raw memory and use it how you want. Powerful stuff. Blah blah blah great power, blah blah blah responsibility. In other words, you have the power to seriously crash your computer/iDevice. And you probably will. So get over it. It’s not going to melt or explode. But you may have to reboot it every once in a while until you learn the ropes.

Basically, you call malloc as a function, passing in how many bytes of memory you would like. The system reserves that chunk of memory for you and hands you back a pointer to it. You should cast this data as a pointer to the type of data you are storing in it. Like so:

[c]int *myInts = malloc(numberofbytestoallocate);[/c]

So how many bytes should you allocate? Well, it depends on how many “things” you want to store in there, and how big those “things” are. The first part you probably know, the second you probably don’t. Luckily there’s a sizeof function that will figure that out for you. So you do something like this if you are storing 10 integers:

[c]int *myInts = malloc(sizeof(int) * 10);[/c]

Or if you are storing 50 CGPoints:

[c]CGPoint *myPoints = malloc(sizeof(CGPoint) * 50);[/c]

You get the point. Or the 50 points. (I crack myself up sometimes!)

Now, you can access that chunk of memory with bracket notation:

[c]myInts[0] = 10;
myInts[1] = 20;
myInts[2] = 30;
myInts[3] = 40;
// etc.[/c]

Or:

[c]someNumber = myInts[8];[/c]

Very simple, very low level, very efficient. Also, very dangerous. If you suddenly decide to say something like:

[c]myInts[99] = 50;[/c]

and you only allocated enough space for 10 ints, you have no protection whatsoever. Your program is going to go to whatever location myInts[99] happens to land on and write the int value 50 there, with no regard for any consequences. You’re not going to get a compiler error. You’re not even going to get a runtime error that politely tells you are attempting to access an element of an array that is out of bounds, so sorry for the inconvenience. No, it’s just going to do exactly what you tell it to and claim it was just following orders. Generally, this means your app is going to crash and burn, fast and hard, with no explanation at all. So don’t do it. OK, you’re going to do it. But after a few times, you’ll realize what you did and you’ll do it much less often.

Generally this means that you are going to keep an extra int or two around to serve as indexes. Maybe one to indicate the maximum amount of values you CAN store in the array, and one to indicate how many values actually ARE in there. For example:

[c]#define MAX_THINGS 100
int *things = malloc(sizeof(int) * MAX_THINGS);
int numThings = 10;
for(int i = 0; i < numThings; i++) { things[i] = i; }[/c] You don’t want MAX_THINGS to change, so that’s a good candidate for #define. numThings could change so we’ll leave that as an int. Here, since we are only setting the first 10 out of 100 things, the other 90 will have garbage values. You’re totally safe to read and write them, but don’t expect them to have any meaningful value until you set them. The last thing to mention is to clean up after yourself. You asked for a chunk of memory, and the system was nice enough to give it to you. Actually, it just lent it to you. When you are done, give it back so someone else can use it. Do this using the free function: [c]free(myInts);[/c] Where you do this is wherever it makes sense. If you are just using the array as a local variable in a method, free it up as soon as you are done using it. If it’s a instance variable of a class, free it in the dealloc method. That’s the bare bones of malloc. There might be other things you want to look up. For instance, what if you ask for a big chunk of memory and the system can’t allocate it. Generally 10 ints or 50 CGPoints won’t be a problem, but you might want to look at what happens if malloc fails, to make sure you guard against that. So happy mallocing, happy crashing. Once you get the hang of it, it’s a pretty cool feeling to have that kind of fine level of control over the memory your program is using, especially if you are coming from the ActionScript world, where memory management is a secret black box that occasional posts by Grant Skinner gives you some shadowy concept of what’s going on in there. With C and malloc, you know, because you are calling the shots.

« Previous Post
Next Post »