Search code examples
iosobjective-cmemory-managementnsobjectnsset

Alloc more than one object at the time


I would like to know how to allow more than one object at the time. I am looking for something like NSSet with 50 empty Objects.

The reason for that is that I have a intensive work with a lot of allocations (1.000.000) and it is very time consuming to to it per one bases. (Alloc method is time expensive).


Solution

  • To answer your question directly, there are two runtime functions that allow you to create/destruct an object located in a pre-allocated block of memory:

    /** 
     * Creates an instance of a class at the specific location provided.
     * 
     * @param cls The class that you wish to allocate an instance of.
     * @param bytes The location at which to allocate an instance of \e cls.
     *  Must point to at least \c class_getInstanceSize(cls) bytes of well-aligned,
     *  zero-filled memory.
     */
    OBJC_EXPORT id objc_constructInstance(Class cls, void *bytes) 
        __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
        OBJC_ARC_UNAVAILABLE;
    
    /** 
     * Destroys an instance of a class without freeing memory and removes any
     * associated references this instance might have had.
     */
    OBJC_EXPORT void *objc_destructInstance(id obj) 
        __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_0)
        OBJC_ARC_UNAVAILABLE;
    

    Totally untested example usage (ARC must be disabled in the @implementation file):

    + (void)allocInPlace:(void *)buffer
    {
        objc_constructInstance(self, buffer);
    }
    
    - (void)dealloc
    {
        objc_destructInstance(self);
    }
    
    size_t instanceSize = class_getInstanceSize(MyClass.class);
    
    // This may not be enough, since we need to ensure that object instances are 16-byte aligned
    void *buffer = calloc(instanceSize, numObjects);
    
    for (size_t i = 0; i < numObjects; i++) {
        // Need to ensure that this is 16-byte aligned
        void *objBuffer = buffer + (instanceSize * i); 
        [mySet addObject:[[MyClass allocInPlace:objBuffer] init]];
    }
    

    You'll need to hang onto buffer and free() it when no longer needed.

    However you should probably just use a C array of structs if possible. The technique I've used here runs against standard Obj-C practice and is error-prone (if, for example, you free the buffer before all objects have been destructed then Bad Things will happen).