Allocation functions
Edit me

Intro

Heap memory management in C is done with malloc and free. These functions can also be used in Objective-C. To get a temporary, autoreleased memory block, you can use [[NSMutableData dataWithLength:] mutableBytes].

MulleObjC provides many memory functions for convenience, better leak checking and more control.

struct mulle_allocator

First up, every allocation in mulle-objc is done through a mulle-allocator. Using the (hidden) mulle_default_allocator, C code simplifies from

p = alloc( 1848);
if( ! p)
{
   perror( "malloc:");
   exit( 1);
}

to

p = mulle_malloc( 1848);

If you want to know why and how this works, read the mulle-allocator README.md. For leak checking refer to the mulle-testallocator README.md

Creating autoreleased and zeroed memory

If NSMutableData is not available, you can use MulleObjCCallocAutoreleased to create an autoreleased and zeroed block of memory. Note that the block will be reclaimed by the enclosing NSAutoreleasePool.

p = MulleObjCCallocAutoreleased( 1, sizeof( struct whatever));

Creating instances

The usual Objective-C way to create an instance is +new or a combination of +alloc and an -init method. The actual allocation is hidden inside the +new/+alloc methods.

Nomenclauture: Classes create instances. Classes and instances are objects.

The default implementation of +alloc is:

+ (instancetype) alloc
{
   return( _MulleObjCClassAllocateInstance( self, 0));
}

You can call _MulleObjCClassAllocateInstance directly, to create an instance. This can be useful, when writing your own +alloc or +new method.

Calling _MulleObjCClassAllocateInstance on a class not under your control, may create unexpected side-effects or errors. You need to consider, if the class is a TPS class or a Singleton class or a Classcluster or any class that may do something special in +alloc.

_MulleObjCClassAllocateInstance destills down to the following code:

   struct mulle_allocator   *allocator;

   allocator = _mulle_objc_infraclass_get_allocator( infra);
   return( __mulle_objc_infraclass_alloc_instance_extra( infra, extra, allocator));

Each class has its own allocator, that is used to allocate instances. Usually this is the mulle_default_allocator. But that may vary on a per-class basis.

Allocating extra memory for an instance

If your instance needs some extra memory to store data, it could use a NSMutableData instance variable:

@interface Foo : NSObject
{
   NSMutableData   *_buffer;
}

- (void *) bytes;

@end


- (instancetype) initWithLength:(NSUInteger) length
{
   _buffer = [[NSMutableData alloc] initWithLength:length]
   return( self);
}


- (void *) bytes
{
   return( [_buffer mutableBytes]);
}


- (void) dealloc
{
   [_buffer release];
   [super dealloc];
}

A disadvantage of this scheme is, that you don’t have control over the allocator used to create the memory. That control lies with NSMutableData. To use the allocator, the instance was created with, use MulleObjCInstanceAllocateMemory:

@interface Foo : NSObject
{
   void   *_buffer;
}

- (void *) bytes;

@end


- (instancetype) initWithLength:(NSUInteger) length
{
   _buffer = MulleObjCInstanceAllocateMemory( self, length);
   return( self);
}


- (void *) bytes
{
   return( buffer);
}


- (void) dealloc
{
   MulleObjCInstanceDeallocateMemory( self, _buffer);
   [super dealloc];
}
MulleObjC Instance Function C equivalent
MulleObjCInstanceAllocateNonZeroedMemory malloc
MulleObjCInstanceReallocateNonZeroedMemory realloc
MulleObjCInstanceAllocateMemory calloc
MulleObjCInstanceDuplicateUTF8String strdup
MulleObjCInstanceDeallocateMemory free

The same functions are also available, with Class as the first parameter. These can be used in + class methods.

MulleObjC Class Function C equivalent
MulleObjCClassAllocateNonZeroedMemory malloc
MulleObjCClassReallocateNonZeroedMemory realloc
MulleObjCClassAllocateMemory calloc
MulleObjCClassDuplicateUTF8String strdup
MulleObjCClassDeallocateMemory free

extraBytes and metaExtraBytes

An instance in memory looks like this:

The address returned by alloc is not the beginning of the memory block allocated for the instance. It is the address after the isa pointer. The memory block is divided into the user accessible ivars and extraBytes “self” block and the “meta” block with negative offsets from “self”.

The “extraBytes” are the second parameter of _MulleObjCClassAllocateInstance. Each instance can therefore have a unique size. The amount of metaExtraBytes is fixed for every class and instance at the start of the program. Currently this is an experimental and unused mulle-objc-runtime feature.

Creating an instance in pre-allocated memory

If you have sufficient memory already allocated, you can use MulleObjCClassConstructInstance to turn this memory into one or as many instances as can fit.

MulleObjCClassGetInstanceSize calculates the size needed for the memory allocation and MulleObjCClassConstructInstance zeroes the memory and initializes isa and the retainCount.

size_t   size;
void     *block;
Class    myClass;
id       obj;

...
size  = MulleObjCClassGetInstanceSize( myClass);
block = my_malloc(size);
obj   = MulleObjCClassConstructInstance( myClass, block, size, NO);
...
my_free( block);  // not obj!

There are a lot of caveats:

  • ascertain that -dealloc doesn’t interfere with your memory scheme
  • ascertain that all instance variables are freed before deallocing
  • calling -init may trigger -release and therefore -dealloc in an error case

Reasonably, this scheme can only be used for very simple value type objects.

Tidbits

If you want to create an object, whose property objects are allocated with the same custom allocator as the owner object, you will have to subclass a lot.

Tags: language