I had some criticisms of
this post, and was asked to write a blog post of my own. So I'm doing that, only it turns out that this topic is absolutely enormous, so I'm going to split it into several posts.
A quick note on terminology: C++ and Objective-C occasionally use different terms to refer to the same concept (e.g., member variables in C++ vs instance variables in Objective-C). I will attempt to use the convention appropriate to the language when referring to them.
Memory
Objective-C and C++ share some conceptual memory traits, mostly the ones inherited from C. There is static allocation and a stack and a heap, primitives and structures can be allocated in any of those storages, and pointers typically though not exclusively point to locations in the heap. Here the similarities mostly end. In C++, objects can either be stack-allocated or heap-allocated, in Objective-C, objects can only be heap-allocated. In fact, Objective-C objects can never be on the stack at all; any attempt to dereference it (though not to access its instance variables) to put it in stack storage is a compile error. For example:
NSString str = *[[NSString alloc] initWithUTF8String:"Foo"]; //error
NSString *str2 = @"foo"; //ok
str2 = [[NSString alloc] initWithUTF8String:"foo"]; // ok
*str2; //error
id someObj = someFunctionReturningAnObject(); //ok
*someObj; // error
id foo = someObj->anIvar; // okay.
Whereas in C++:
std::string str("Foo"); //ok
std::string *str2 = new std::string("Foo"); //ok
str = "bar"; //ok
*str2; // ok
*str2 = str; //ok but leaks
Stack allocations are much faster than heap allocations; over and above actually constructing the object, stack allocations only involve moving the stack pointer (and possibly setting the contents of memory to 0 in between where it was and where it is now) while heap allocations involve traversing a data structure searching for a free block of memory large enough to hold our object (see
this article and the pages it references).
Stack objects are not useful everywhere in C++; you can assign a stack object into a member variable, but it will call the assignment operator of that member variable, which may copy the member variables of the assignee and do other unexpected computation or allocation. In contrast, assigning a pointer into a pointer-type member variable is a matter of copying the pointer, no more expensive than copying an int[1]. There's also a gotcha if you have a stack object, the value of which you want to put into a pointer-type member variable. If you just take the address of the object and assign that to the pointer, your program will crash:
class Foo {
Bar *b;
void doIt() {
Bar bar;
b = &bar;
}
void andAnotherThing() {
b->whateverBarsDo(); //crash!
}
}
This behavior is the same as assigning into a pointer in a struct in C; the point is that it hasn't been fixed in C++.
In Objective-C, except for the standard C primitive types, there are no stack objects, so it's impossible to make these sorts of mistakes with object pointer instance variables.
As touched on briefly above, static allocation exists in both languages as well, however only the C data types and pointers can have static storage in Objective-C, whereas other objects can as well in C++.[2] For Objective-C this does include object pointers, so you'll often see declarations like:
static NSString *const kMYClassImplInternalString = @"I'm a String!"; //technically this is both uses of static, storage and file scope
NSString *const MYClassStringForExport = @"I'm a string too!"; // this is static storage, global scope
//or
...
+ (NSDictionary *)sharedDict
{
static NSDictionary *onceMap = nil; //static storage, method scope
static dispatch_once_t token;
dispatch_once(&token,
^{
onceMap = [[NSDictionary alloc] initWithObjectsAndKeys:...];
});
return onceMap;
}
...
There are memory management differences at higher levels too. C++ has many different memory management models, often varying in type and implementation by library, including reference counting (standardized in C++11 with shared pointers, but other implementations still exist), ad-hoc tracking of pointers, shared data, interior pointers... the list goes on. In Objective-C, there is only one - reference counting - with two implementations, Manual and Automatic. These are defined by the library and runtime (though the mechanics of ARC take place at compile time), although in practice, you'll rarely see deviations from them. Although individual implementations differ in C++, reference counting essentially works as follows: when an object is created, it has a reference count (retain count in ObjC) of 1, and every time the object pointer is stored, that count is incremented. When the pointer itself is deallocated (either because it is popped off the stack, or because the object containing it was deallocated), the reference count is is decremented. Under MRC in Objective-C, the retainCount can also be manually increased or decreased by sending -retain and -release messages to the object.
Objective-C also has -autorelease, which adds the pointer to the topmost autorelease pool in a stack; when the stack is popped, all the objects in the pool are sent a -release message. At runtime there exists a runloop in all Cocoa applications (and most other Objective-C applications) that will catch all autoreleased objects not put in an explicitly declared pool; at the bottom of this runloop, this pool is popped and recreated. Under ARC, autoreleasing is automatic, however additional autorelease pools beyond the provided one still have to be declared, which is done with the @autoreleasepool{} directive[3]. While runloops are common in C++ applications and frameworks, I don't know of anything directly equivalent to autorelease pools, though it's certainly possible they exist in some libraries.
Objective-C has support for weak references as well - object pointers that do not retain the object stored into them, and which become nil immediately upon deallocation of the object elsewhere in the program. This the main means by which retain cycles (which cause memory leaks) are avoided in Objective-C. Weak references could be implemented in C++ using operator overloading and functions akin to the Objective-C runtime functions that provide it, but I don't know of any implementation of this (technically raw pointers are "weak", but are do not NULL themselves out when the objects are destroyed).
Lastly on memory: garbage collection. C++ doesn't have it. Objective-C had it, on the Mac (but not iOS) platform between Mac OS X 10.5 and 10.7, but as of 10.8, garbage collection is deprecated in favor of ARC for performance and determinism[4] reasons, so I won't say anything more about it.
Pedantic notes:
[1] No, this isn't the case on machines where the size of an int is not the size of a pointer. Substitute your pointer-size word where appropriate.
[2] NSString literals in Objective-C have static storage as well because where else are you going to put the string data and then because it makes sense as a performance optimization to store them in the image in their "native" in-memory format, but you can still only refer to them through pointers.
[3] This used to be done with
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[pool drain]; //or, even older, [pool release];
however the @autoreleasepool syntax ensures that the pool drains when a particular scope ends, and that premature returns or breaks don't leak the pool and the objects in it. As the directive is backwards-compatible, there's no reason to use the old version. The directive is also faster - there's no longer any efficiency reason to wait for some number of objects to build up in the pool before draining it, so you can use it inside tight loops.
[4] There was a problem with non-collectable interior pointers in collectable objects when exposed, in that the use of the interior pointer would not prevent the object from being collected, and so it was to the user nondeterministic when or whether the following code would crash:
- (void)someMethod
{
NSData *data = someFunctionReturningSomeData();
unsigned char *bytes = [data bytes];
unsigned int len = [data length];
for (unsigned int i = 0; i < len; i++)
{
// do something with bytes[i];
}
}