Правильный способ освободить объект, который реализует NSOperationQueue и асинхронные запросы

#objective-c #memory-management #asihttprequest #nsoperationqueue

#objective-c #управление памятью #asihttprequest #nsoperationqueue

Вопрос:

Я изо всех сил пытаюсь найти решение моей проблемы,

У меня есть класс загрузки, который обрабатывает вызовы моего api, эти вызовы добавляются в NSOperationQueue. Каждому вызову присваивается уведомление о завершении и сбое, которое публикуется при завершении или сбое вызова. Затем я могу корректно обработать завершение / сбой запроса в моем контроллере просмотра.

Проблема, с которой я сталкиваюсь, заключается в следующем, каков правильный способ выделить / инициализировать / освободить мой класс загрузки. Мой первый подход был таким:

выделять инициализацию нового экземпляра класса загрузки каждый раз, когда мне нужно выполнить запрос, тогда у меня может быть уникальный экземпляр класса с уникальными уведомителями о завершении и сбое и другими параметрами, как я пожелаю. Проблема, с которой я столкнулся при таком подходе, заключается в том, когда / как освободить объект. Я не могу просто вызвать запрос на выборку, а затем освободить объект в рамках одного вызова, поскольку у вызова download есть очередь для завершения и уведомления для отправки, я знаю, когда экземпляр класса download завершил свои вызовы из-за уведомления, я просто не знаю правильного способа реализовать его освобождение из другой функции. например:

 -(void)downloadLists:(int)page featured:(BOOL)featured {

    NSMutableDictionary *postValues = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:page],@"page",@"false",@"is_featured", nil];

    if(featured){
        [postValues setValue:@"true" forKey:@"is_featured"];
    }

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *destination = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"lists_%i.json",page]];

    Download *download = [[Download alloc] init];
    [download setCompleteNotifier:@"listsComplete"];
    [download setFailedNotifier:@"listsFailed"];
    [download downloadPOST:[NSURL URLWithString:@"http://blahblah"] values:postValues destination:destination];

}
  

Тогда, где мне освободить загрузку и убедиться, что я выпускаю правильный экземпляр, downloadLists может вызываться (n) количество раз в быстрой последовательности.

Мой другой подход заключался в использовании одноэлементного метода загрузки, который был отличным, пока мне не понадобилось добавить userinfo в уведомления, что, конечно, запуталось из-за того, что класс singleton вызывался из разных мест.

Любая помощь будет высоко оценена, вот функция downloadPOST для вашей справки:

 -(void)downloadPOST:(NSURL *)path values:(NSDictionary *)keyValues destination:(NSString *)destination {

    ASIFormDataRequest *formRequest = [ASIFormDataRequest requestWithURL:path];

    for(id key in keyValues){
        [formRequest setPostValue:[keyValues objectForKey:key] forKey:key];
    }

    [formRequest setDownloadDestinationPath:destination];
    [formRequest setDelegate:self];
    [formRequest setDidFinishSelector:@selector(requestDone:)];
    [formRequest setDidFailSelector:@selector(requestWentWrong:)];
    [queue addOperation:formRequest];

}
  

Ответ №1:

Очередь должна быть той, которая обрабатывает освобождение объекта. Здесь есть несколько вариантов.

  1. Вы можете использовать NSNotification для публикации вашего конечного состояния, которое прослушивает очередь, и удаления экземпляров, отправляющих это уведомление

  2. Вы можете создать частное делегирование между загрузкой и очередью, где очередь получает уведомление

  3. Вы можете установить логическое свойство @ в загружаемом классе (т. е. BOOL completed ). В очереди сохраняется экземпляр загрузки и добавление прослушивателя к завершенному KVC. Когда для параметра completed установлено значение YES, объект освобождается

AFAK, вы используете ASIHTTPRequest, который обрабатывает все это за вас, поэтому я не уверен, зачем вы это переделываете

Комментарии:

1. Хм, однако моя проблема заключается в освобождении объекта, содержащего очередь, если вы не предлагаете мне вызвать [self release], когда очередь завершена?

2. Извините, забыл об этом вопросе : ( Во-первых, вы никогда не вызываете release самостоятельно, это не имеет смысла. Я думаю, вам было бы лучше использовать синглтон или статическую переменную, доступную для всего вашего загружаемого класса, некоторое время назад я опубликовал в своем блоге сообщение именно об этом варианте использования. Возможно, стоит взглянуть: pothibo.com/2011/02/06 /…

Ответ №2:

Похоже, что вы оборачиваете функциональность, уже включенную в ASIHTTPRequest по умолчанию. В классы ASI встроены делегаты для уведомления вас о завершении или сбое:

 - (void)requestFinished:(ASIHTTPRequest *)request;
- (void)requestFailed:(ASIHTTPRequest *)request;
  

Есть ли какая-либо другая причина, по которой вы не можете просто создать запрос в своем основном классе без создания класса загрузки, а затем позволить делегату сообщить вам, когда это будет сделано?

Комментарии:

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