56

I've been playing around with iPhone development for a while, and although it feels a bit awkward when you're a "hard core" .NET developer, it's not all that bad once you get used to it.

In every book I read about Objective-C, there's only talk about retain/release (reference counting) for memory management. As an old-skool C/C++ developer, it seems strange that allocating the "normal" way, using malloc() and free() is only mentioned in some footnotes.

I know that malloc() and free() work in Objective-C, but I'm curious if it is common practice or not. After all, if I want to allocate an array of 100 integers, it seems that this is the most efficient way to do it:

int *array = malloc(sizeof(int) * 100);

memset(array,0,sizeof(int) * 100);

// use the array

free(array);

Is this indeed the best way, or should I avoid plain C memory management?

ricardopereira
  • 11,118
  • 5
  • 63
  • 81
Philippe Leybaert
  • 168,566
  • 31
  • 210
  • 223

5 Answers5

87

There's an Objective-C wrapper around raw memory which I like to use a lot for similar tasks: NSMutableData. It has the benefit of giving you retain/release ownership plus it can grow the array easily (without having you to do the realloc yourself).

Your code would look like:

NSMutableData* data = [NSMutableData dataWithLength:sizeof(int) * 100];
int* array = [data mutableBytes];
// memory is already zeroed

// use the array

// decide later that we need more space:
[data setLength:sizeof(int) * 200];
array = [data mutableBytes]; // re-fetch pointer in case memory needed to be copied

// no need to free
// (it's done when the autoreleased object is deallocated)
Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • I like this answer @Nikolai Ruhe. I was using a "malloc"ed array as a property. I had 3 objects of this class, and I "free"ed the array in dealloc. However after releasing the first object, I got a "malloc: *** error for object 0x70a60: pointer being freed was not allocated" error when releasing the second!? Looks like the malloc was done at "class level", rather than "object level". Switching to your solution made this go away, but the "behaviour" of the objects also changed. Not yet sure why. – iPadDeveloper2011 Mar 02 '11 at 02:16
  • OK, I tried this and, to my horror, eventually found that my "array" filled-up with random bits. I guess the underlying "data" was autoreleased before I had finished with the array. Putting a "retain" on the data fixed that problem, but now I have to keep a reference to the "data" object, making this solution less attractive. – iPadDeveloper2011 Mar 02 '11 at 04:24
  • OK @Nikolai Ruhe, this is a really horrible answer that wasted almost my whole day. Even if `data` is an "object variable", it appears to get autoreleased, allowing `array` to fill-up. The only way to get malloc-like behaviour appears to be to `[data retain];` which makes this a very misleading answer. A better way to use `NSMutableData` then would be to start with `NSMutableData* data = [[NSMutableData alloc] initWithLength:sizeof(int) * 100];` – iPadDeveloper2011 Mar 02 '11 at 04:32
  • OK, I can confirm that using `NSMutableData* data = [[NSMutableData alloc] initWithLength:sizeof(int) * 100];` and then releasing later works like `malloc` and `free`. Also, the problem with using malloc/free I mention above seems to have been caused by calling `[super dealloc];` at the beginning of the `dealloc` method, rather than the end. – iPadDeveloper2011 Mar 02 '11 at 04:59
  • 5
    @iPadDeveloper2011 You have to re-read the Memory Management Programming Guide at http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html – Nikolai Ruhe Mar 02 '11 at 09:06
  • 1
    @iPadDeveloper2011: You need to brush up on your memory management. In this particular case, the data is being autoreleased. So long as the autorelease pool isn't drained, this code is fine, but once you reach a scope where there might be an autorelease pool that could drain, you can no longer use the data. If you need it to last longer, then you must `retain` it, and then `release` it when you're done. – Adam Rosenfield Mar 02 '11 at 15:49
  • @iPadDeveloper2011: And as for your problem with `malloc`, you again are doing it wrong. The error message says you're trying to call `free` on a pointer that was not allocated with `malloc`, which is a serious error; it's also possible you're calling `free` more than once on the same pointer, which is just as wrong. Make sure that when you copy your objects, you're doing a deep copy (allocating new memory and copying the contents, as opposed to just copying the pointers). – Adam Rosenfield Mar 02 '11 at 15:51
  • OK @Nikolai Ruhe, @Adam Rosenfield. Thanks for comments. I'm used to my objects staying around either: for the duration of a block of code; or (for dynamic memory) precisely as long as I tell them to. I recall those simple, bygone days with fondness and regret. I read http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html and now understand that autorelease objects are far less useful than I thought: you can't bank on them persisting for any length of time, nor can you bank on them releasing short term. – iPadDeveloper2011 Mar 06 '11 at 12:42
  • @Adam Rosenfield, you didn't read my comment before replying: "Also, the problem with using malloc/free I mention above seems to have been caused by calling [super dealloc]; at the beginning of the dealloc method, rather than the end." – iPadDeveloper2011 Mar 06 '11 at 12:46
  • @Nikolai, considering this is an answer for a malloc/free question, I think your code would be better with `[NSMutableData alloc]initWithLength ...` ... `[data release]` in it. Although I guess what you have might be useful for an "instant use, throw-away memory" type scenario. – iPadDeveloper2011 Mar 06 '11 at 12:56
  • 3
    I for one love this answer. I've always been bothered when I need to use arrays of ints and lose the reference-counting, and being able to easily extend them. Thanks for the very good explanation and examples! – Accatyyc Feb 21 '12 at 20:56
  • This is probably the best way to handle such problems. – Cameron Lowell Palmer Aug 03 '16 at 14:34
48

It's perfectly fine -- Objective-C is a strict superset of C, so if you want to write plain C, there's nothing preventing you from doing so. In many cases, it's advantageous to use malloc and free to avoid the overhead of the Objective-C runtime.

For example, if you need to dynamically allocate an array of an unknown number of integers, it's often simpler and easier:

int *array = malloc(N * sizeof(int));  // check for NULL return value!
// use array[0]..array[N-1]
...
free(array);

Versus:

NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:N];
// use NSMutableArray methods to do stuff with array; must use NSNumbers instead
// of plain ints, which adds more overhead
...
[array release];

I was working on a word game for the iPhone, and we had to load a multi-megabyte dictionary of valid words. The word list was loaded into one giant char array allocated with malloc(), with some clever optimizations to reduce the memory size even more. Obviously for something like this, the overhead of using an NSArray is completely impractical on the limited iPhone. I don't know exactly what the overhead is, but it's certainly more than one byte per character.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • 6
    RE: 'strict superset'. While I personally (strongly) agree with you, since we're discussing Apple/iPhone development, this statement is not technically true. Apple defines it thusly: `Objective-C syntax is a superset of GNU C/C++ syntax`. Strict superset has a very specific meaning, with the unqualified use of superset being the lesser of the two (think "behaves like" vs. "exactly like"). The qualifier of `syntax` further restricts it to the point where it is almost useless, effectively limiting Apples obligation to Annex A of the C99 spec- a scant 16 pages out of 552 in the standard. – johne Jul 20 '09 at 05:49
  • I've had a number of problems using malloc/free in Objective C. See my comment to Nikolai's answer. Apart from that, I've had problems with assigning (copying) pointers to malloced arrays to share the malloced array between objects. – iPadDeveloper2011 Mar 02 '11 at 02:25
  • While it's true that you can use malloc() and free(), you can avoid most of the runtime's overhead by using a NSMutableData of appropriate length instead. – Steven Fisher May 02 '14 at 02:34
5

Of course, you can use these functions, because Objective-C is merely a superset of C. However, it is fairly uncommon to do this sort of thing, since Objective-C contains objects and ways to make this easier.

After all, you could write the above code as:

NSMutableArray *array = [[NSMutableArray alloc] init];

//Use the array, adding objects when need be

[array release];

Although you would have to create NSNumber objects to store the ints (since NSArray doesn't allow non-object types to be added), it is generally more common to use objects, because it is easier to move data around, and the array classes are integrated more commonly with other Cocoa classes, and the memory management is generally more straightforward than standard C memory management.

Also, if you start adding or removing objects from the array, then the Cocoa array objects make this much easier to do.

Alex Rozanski
  • 37,815
  • 10
  • 68
  • 69
  • 2
    This seems like overkill if you need a simple array of integers. Especially the need to create NSNumber objects seems so inefficient to me. What if I want to allocate an array of 100,000 booleans? – Philippe Leybaert Jul 19 '09 at 19:35
  • Perhaps, there may be a slight overhead compared to using simple arrays of integers. But they are certainly more commonly used than using C memory management. And if you are allocating an array of 100,000 booleans then there may be a better way to do it than how you are currently implementing it (unless that's a hypothetical scenario). – Alex Rozanski Jul 19 '09 at 19:41
  • 1
    This is especially overkill if you're dealing with really simple objects. For example, if you were to build MineSweeper for the iPhone, it is *orders of magnitude* faster to have a square be a struct and malloc an array of structs than to create the squares as objects and put them in an NSArray. Plus, you'll use quite a bit less memory. – Dave DeLong Jul 19 '09 at 19:41
  • Of course, it depends on the context of the question, but using standard C memory management is fairly uncommon. Also, as I said, they can become more useful if you are manipulating the array items. – Alex Rozanski Jul 19 '09 at 19:44
  • I, too, disagree that it is not common. It depends very much on the problem domain. For example when using C libraries (like OpenGL, which is very common) you have to dig into C memory management. Plus it is far more efficient for large arrays pod types. – Nikolai Ruhe Jul 20 '09 at 16:16
  • 1
    Just want to point out that an array of 100,000 booleans (BOOLs?) is already a memory inefficient solution, because each BOOL is 1 byte, but you really only need 1 bit. So about 8x better to use an array of 100,000/8 chars, and bitwise operators. – iPadDeveloper2011 Mar 02 '11 at 02:59
3

If you're dealing with standard C types, it's no less common or "OK" than in C. That's how it's done in C, which is a part of Objective-C.

It's also not unusual to write some kind of object wrapper around these things to bring it into harmony with the rest of Cocoa (KVO, memory management, etc.). So you might create an IntArray class that does the mallocing behind the scenes so you can retain and release it as needed. Note that this isn't strictly necessary — it can just be handy if that kind of structure is a major part of your program.

Chuck
  • 234,037
  • 30
  • 302
  • 389
2

It's perfectly fine to use malloc and free to do your own memory management. Actually NSObject's allocWithZone: uses malloc to get the memory.

Nikolai Ruhe
  • 81,520
  • 17
  • 180
  • 200
  • 1
    It's technically calloc, but yeah. =) – Dave DeLong Jul 19 '09 at 19:43
  • 7
    Actually, it's technically `NSAllocateObject()`. What happens next is more complicated. Under ObjC2 w/ GC enabled, `NSAllocateObject()` calls `objc_allocate_object()`. Under ObjC2 w/o GC, or ObjC < 2, `NSAllocateObject()` calls `class_createInstanceFromZone()`, which in turn calls `malloc_zone_calloc()`, which, as its name implies, is logically equivalent to `calloc()`. A `calloc()` with a `count` of `1` is by definition indigistqushable from an allocation obtained by `malloc` of the same `size` that has had its `space is initialized to all bits zero` (C99 7.20.3.1.2). – johne Jul 20 '09 at 05:17