Использование NSOperationQueue для отложенного освобождения от malloc'd void * после его возвращения - PullRequest
4 голосов
/ 15 февраля 2012

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

В следующей реализации я попытался освободить его с помощью блока, который выполняется в следующем цикле выполнения, но я получаю сбой из-за того, что 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] 1023 **

 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;

1 Ответ

2 голосов
/ 15 февраля 2012

Вы можете получить void * из NSMutableData, точно так же, как вы можете malloc(), и, по сути, превратить его в переменную экземпляра, так что время жизни выделения будет привязано к времени жизни экземпляра. Я получил этот трюк от Майка Эша . В последнее время я занимался обезьяной. это относится к категории 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 иваром.

Свяжите это в свой код:

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() не считает он владеет. Таким образом, вы получаете ошибку о том, что вы ее не разместили.

...