I’m using NSInvocation to get some method returns, and unfortunately I seem to have a leak, but can’t figure out how to free the void* I’m allocating, after I’ve returned it from NSInvocation.
In the following implementation I tried to free it with a block that gets performed on the next run loop, but I get a crash due to returnBuffer not being allocated.
Why can’t I free returnBuffer in the block, and if it hasn’t been allocated why is it getting through returnBuffer!=NULL?
This is a special method that has to do with IMP swizzling so I DON’T know the method return type. Putting it in NSData or something will not work.
NSUInteger length = [[invocation methodSignature] methodReturnLength];
if(length!=0){
void* returnBuffer = (void *)malloc(length);
[invocation getReturnValue:&returnBuffer];
if(returnBuffer!=NULL){
void(^delayedFree)(void) = ^{ free(returnBuffer); };
[[NSOperationQueue mainQueue] addOperationWithBlock:delayedFree];
}
return returnBuffer;
}
return nil;
ANSWER
Got it to work the following way thanks to Josh’s -[NSMutableData mutableBytes] trick
NSUInteger length = [[invocation methodSignature] methodReturnLength];
if(length!=0){
NSMutableData * dat = [[NSMutableData alloc] initWithLength:length];
void* returnBuffer = [dat mutableBytes];
[invocation getReturnValue:&returnBuffer];
void(^delayedFree)(void) = ^{ [dat release]; };
[[NSOperationQueue mainQueue] addOperationWithBlock:delayedFree];
return returnBuffer;
}
return nil;
You can get a
void *fromNSMutableData, just like you can frommalloc(), and essentially turn it into an instance variable, so that the lifetime of the allocation is tied to the lifetime of the instance. I got this trick from Mike Ash. I’ve been doing someNSInvocationmonkeying myself lately; this is in a category onNSInvocation(you should use your own prefix for the method, of course):I’m not sure where the code you’ve posted is located; if you’re in your own custom class, you don’t need to deal with the associated objects bits — just make
allocationsan ivar.Tying this into your code:
If you use the associated object route, the
allocationsarray will be deallocated when this instance is. Otherwise, just put[allocations release]into yourdealloc.Also, to answer your question as posed, rather than just solving the problem:
free()won’t operate on any pointer that you didn’t get frommalloc(). When the pointer gets used in the Block, I’m pretty sure it gets copied, so you end up with another pointer — still pointing to the same memory, but one thatfree()doesn’t think it owns. Thus, you get the error about not having allocated it.