Free and malloc with NSValue and NSInvocation

I am making a utility to get / set property values ​​when a property has custom getters and setters. You can see the full context on line 279 here . The relevant snippet is here:

- (id) getFrom:(id) object {
    NSMethodSignature *methodSig = [[object class] instanceMethodSignatureForSelector:[self getter]];
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:methodSig];
    [inv setSelector:[self getter]];
    [inv setTarget:object];
    [inv invoke];

    if ([self isObject]) {
        id returnValue;
        [inv getReturnValue:&returnValue];
        return returnValue;
    } else {
        void *buffer;
        NSUInteger length = [methodSig methodReturnLength];
        buffer = (void *)malloc(length);
        [inv getReturnValue:buffer];
        NSValue *value = [NSValue valueWithBytes:buffer objCType:[methodSig methodReturnType]];
        //FIXME: Memory leak for buffer!  But if we free it, [value getValue:] is a dangling pointer.
        //free(buffer)
        return value;
    }
}    

      

The problem is that when the property is a scalar, I want to return an NSValue (similar to encoding a Key-Value). However, the return value for NSInvocation is returned by reference, and according to apple documentation (see below), I cannot free the memory associated with the scalar while NSValue still exists, but I am returning NSValue, so I don't know when to free memory.

Am I reading the documentation wrong? Does NSValue handle this automatically? Or how can I properly free the memory in this situation?

+3


source to share


2 answers


You should be free buffer

. +valueWithBytes:objCType:

copies the buffer sent, so you can free the buffer when you're done.

You can also use an object NSData

here, for example:

NSData *data = [[NSData alloc] initWithBytesNoCopy:buffer length:length freeWhenDone:YES]; 

      



This means that when the NSData object is freed, a buffer will be created.

Proof that +valueWithBytes:objCType:

copies the buffer:

int *buffer = malloc(sizeof(int));
buffer[0] = 50;

NSValue *value = [NSValue valueWithBytes:buffer objCType:@encode(int)];

// sribble 'buffer'
buffer[0] = 1000;
free(buffer);

int test = 1000;
[value getValue:&test];

printf("%i", test);  // outputs 50

      

0


source


I suggest using NSMutableData

instead malloc()

. You can allocate some memory

NSMutableData * dat = [NSMutableData dataWithLength:length];

      

and use it the same as malloc

' d chunk

void * buffer = [dat mutableBytes];

      

Now dat

owns memory which puts it under the normal Cocoa reference counting scheme. From now on, you can do one of two things.



If this object is short-lived and won't make many such allocations, an NSMutableArray

ivar called allocations

or similar can contain all instances NSMutableData

and they will be freed if the object whose method it is freed.

If you prefer, you can link life NSMutableData

to yourself, NSValue

or NSInvocation

using linked objects :

objc_setAssociatedObject(value, &dat_key, dat, OBJC_ASSOCIATION_RETAIN);

      

Where dat_key

is a nearby declared variable static

whose address you are using (as recommended by the docs).

This makes the associator ( value

in this case) retain the data object and release it when it is freed itself.

0


source







All Articles