2012-02-15 1 views
4

Я использую NSInvocation, чтобы получить какой-либо метод, и, к сожалению, у меня, похоже, есть утечка, но я не могу понять, как освободить void*. m выделения, после того как я вернул его с NSInvocation.Использование NSOperationQueue для отложенного без malloc'd void * после его возврата

В следующей реализации я попытался освободить его блоком, который будет выполняться в следующем цикле выполнения, но я получаю сбой из-за того, что returnBuffer не выделяется.

Почему я не могу освободить returnBuffer в блоке, и если он не был выделен, почему он проходит через returnBuffer!=NULL?

Это специальный метод, связанный с IMP swizzling, поэтому я НЕ знаю тип возвращаемого метода. Поместите его в NSData или что-то не получится.

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; 

ОТВЕТ Понял работать следующим образом благодаря -[NSMutableData mutableBytes] трюка Джоша

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; 
+0

Dat release tho. – immibis

ответ

2

Вы можете получить void * от NSMutableData, так же, как вы можете от malloc(), и, по сути превратить это в переменную экземпляра, так что время жизни распределения привязывается к времени жизни экземпляра. Я получил этот трюк от Mike Ash. В последнее время я делал несколько обезьян: NSInvocation; это находится в категории на NSInvocation (вы должны использовать собственный префикс для данного метода, конечно):

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]; 
} 

Я не уверен, где код Вы разместили расположен; если вы находитесь в своем собственном классе, вам не нужно иметь дело с битами связанных объектов - просто сделайте allocations ivar.

Связывая это в ваш код:

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

Если вы используете соответствующий объект маршрута, то allocations массив будет освобождаться, когда этот экземпляр. В противном случае просто введите [allocations release] в ваш dealloc.

Кроме того, чтобы ответить на поставленный вопрос, а не просто решить проблему: free() не будет работать ни на одном указателе, который вы не получили от malloc(). Когда указатель используется в блоке, я уверен, что он скопирован, поэтому вы получите еще один указатель - все еще указывая на ту же память, но тот, который free() не считает, что он принадлежит. Таким образом, вы получаете ошибку в том, что не выделили ее.

+0

Спасибо, я не совсем понимаю вашу реализацию, но трюк NSMutableData mutableBytes, похоже, именно то, что мне нужно. –

+0

Рад, что это полезно! Я могу попытаться прояснить все, что вы хотите. –

+0

Извините, я должен был сделать это более ясным. Я не «слежу» за вашей реализацией, так как я не реализую ее так же. Вы были очень ясны в своем ответе о вашей реализации. –