Using NSOperationQueue for deferred without malloc'd void * after returning it

I use NSInvocation to get some kind of method, and unfortunately I seem to have a leak, but I can’t figure out how to free void* I allocate after I return it from NSInvocation .

In the next implementation, I tried to free it with a block, which will be executed in the next execution cycle, but I get a failure due to the lack of returnBuffer .

Why can't I free the returnBuffer in the block, and if it has not been allocated, why does it go through returnBuffer!=NULL ?

This is a special method that is associated with IMP swizzling, so I DO NOT know the type of the returned method. Put it in NSData or something will fail.

 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 as follows thanks to Josh -[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; 
+4
source share
1 answer

You can get void * from NSMutableData , as you can from malloc() , and essentially turn it into an instance variable so that the distribution lifetime is tied to the instance lifetime. I got this trick from Mike Ash . Lately I've been doing some NSInvocation monkey; this is in the category on NSInvocation (of course, you should use your own prefix for this method):

 static char allocations_key; - (void *) Wool_allocate: (size_t)size { NSMutableArray * allocations = objc_getAssociatedObject(self, &allocations_key); if( !allocations ){ allocations = [NSMutableArray array]; objc_setAssociatedObject(self, &allocations_key, allocations, OBJC_ASSOCIATION_RETAIN); } NSMutableData * dat = [NSMutableData dataWithLength: size]; [allocations addObject:dat]; return [dat mutableBytes]; } 

I'm not sure where the code you posted is; if you are in your own class, you do not need to deal with bits of related objects - just make allocations ivar.

Including this in your code:

 NSUInteger length = [[invocation methodSignature] methodReturnLength]; if(length!=0){ void* returnBuffer = [self Wool_allocate:length]; [invocation getReturnValue:&returnBuffer]; return returnBuffer; } return nil; 

If you use the linked route of an object, the allocations array will be freed if this instance is deleted. Otherwise, just put [allocations release] in your dealloc .

In addition, to answer the question posed, and not just solve the problem: free() will not work with any pointer that you did not receive from malloc() . When the pointer will be used in the block, I am sure that it will be copied, so you will get another pointer - still pointing to the same memory, but one that free() does not consider that it belongs. Thus, you get an error in not highlighting it.

+2
source

Source: https://habr.com/ru/post/1396552/


All Articles