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?
source to share
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
source to share
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.
source to share