2014-10-06 3 views
2

Я создал небольшое демо-приложение, которое позволяет пользователю выбирать цвет, который отправляется на базовый (для nowhosthost) узел.js-сервера (используя NSURLSessionDataTask), который использует имя цвета для получения имени фрукта и URL изображения и возвращает простой объект свойства JSON, содержащий два объекта.Утечка памяти при вызове NSURLSession и загрузка изображений в NSImage

Когда приложение получает ответ JSON, оно создает предложение с именем цвета и именем фрукта для отображения в графическом интерфейсе, а затем порождает другой вызов NSURLSession (на этот раз с использованием NSURLSessionDownloadTask), чтобы использовать URL-адрес изображения и загружать изображение из фруктов, которые также отображаются в графическом интерфейсе.

В обеих этих сетевых операциях используется [NSURLSession sharedSession].

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

  1. Инициализировать задачу сеанса, передавая блок в качестве обработчика завершения.

  2. Если я правильно понимаю, блок запускается в отдельном потоке, так как связь в NSURLSession по умолчанию асинхронна, поэтому обновление GUI должно происходить в основном, поэтому в блоке fullHandler вызов dispatch_async с указанием основного потока и короткого вложенного блока, который делает вызов для обновления GUI.

Я полагаю, что причиной является проблема использования вложенных блоков или вложенности вызовов GCD. Хотя это вполне возможно, моя проблема многогранна.

Был надеется, что некоторые из вас с более глубоким знанием того, как Obj-C управляет памятью с помощью потоков и ARC, будут очень полезны. Соответствующий код приведен ниже:

AppDelegate.m

#import "AppDelegate.h" 
#import "ColorButton.h" 

@interface AppDelegate() 

@property (weak) IBOutlet NSWindow *window; 

@property (weak) IBOutlet NSImageView *fruitDisplay; 
@property (weak) IBOutlet NSTextField *fruitNameLabel; 

@property (weak) IBOutlet ColorButton *redButton; 
@property (weak) IBOutlet ColorButton *orangeButton; 
@property (weak) IBOutlet ColorButton *yellowButton; 
@property (weak) IBOutlet ColorButton *greenButton; 
@property (weak) IBOutlet ColorButton *blueButton; 
@property (weak) IBOutlet ColorButton *purpleButton; 
@property (weak) IBOutlet ColorButton *brownButton; 

@end 

@implementation AppDelegate 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification 
{ 
    proxy = [[FruitProxy alloc] init]; 

} 

- (void)applicationWillTerminate:(NSNotification *)aNotification 
{ 
    // Insert code here to tear down your application 
} 

-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender 
{ 
    return YES; 
} 

/*------------------------------------------------------------------*/ 

- (IBAction)colorButtonWasClicked:(id)sender 
{ 
    ColorButton *btn = (ColorButton*)sender; 

    NSString *selectedColorName = btn.colorName; 

    @autoreleasepool { 
     [proxy requestFruitByColorName:selectedColorName 
        completionResponder:^(NSString* fruitMessage, NSString* imageURL) 
     { 
      [self fruitNameLabel].stringValue = fruitMessage; 


      __block NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]]; 

      __block NSURLSession *imageSession = [NSURLSession sharedSession]; 
      __block NSURLSessionDownloadTask *imgTask = [imageSession downloadTaskWithRequest:req 
                      completionHandler: 
                  ^(NSURL *location, NSURLResponse *response, NSError *error) 
                  { 

                   if(fruitImage != nil) 
                   { 
                    [self.fruitDisplay setImage:nil]; 
                    fruitImage = nil; 
                   } 

                   req = nil; 
                   imageSession = nil; 
                   imgTask = nil; 
                   response = nil; 


                   fruitImage = [[NSImage alloc] initWithContentsOfURL:location]; 
                   [fruitImage setCacheMode:NO]; 


                   dispatch_async 
                   (
                   dispatch_get_main_queue(), 
                   ^{ 
                    [[self fruitDisplay] setImage: fruitImage]; 
                   } 
                   ); 

                  }]; 

      [imgTask resume]; 

     }]; 
    } 


} 


@end 

FruitProxy.m

#import "FruitProxy.h" 

@implementation FruitProxy 


- (id)init 
{ 
    self = [super init]; 

    if(self) 
    { 
     return self; 
    } 
    else 
    { 
     return nil; 
    } 
} 


- (void) requestFruitByColorName:(NSString*)colorName 
      completionResponder:(void(^)(NSString*, NSString*))responder 
{ 
    NSString *requestURL = [self urlFromColorName:colorName]; 
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:requestURL]]; 

    session = [NSURLSession sharedSession]; 


    @autoreleasepool { 
     NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler: 
             ^(NSData *data, NSURLResponse *response, NSError *connectionError) 
             { 

              NSString *text = [[NSString alloc] initWithData:data 
                       encoding:NSUTF8StringEncoding]; 

              NSDictionary *responseObj = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 
              NSString *fruitName = (NSString*)responseObj[@"fruitName"]; 
              NSString *imageURL = (NSString*)responseObj[@"imageURL"]; 

              NSLog(@"Data = %@",text); 

              dispatch_async 
              (
              dispatch_get_main_queue(), 
              ^{ 
               responder([self messageFromColorName:colorName fruitName:fruitName], imageURL); 
              } 
              ); 
             }]; 

     [task resume]; 
    } 

} 


- (NSString*)urlFromColorName:(NSString*)colorName 
{ 
    NSString *result; 

    result = @"http://localhost:9000/?color="; 
    result = [result stringByAppendingString:colorName]; 

    return result; 
} 

- (NSString*)messageFromColorName:(NSString*)colorName 
         fruitName:(NSString*)fruitName 
{ 
    NSString *result = @"A "; 

    result = [[[[result stringByAppendingString:colorName] 
         stringByAppendingString:@"-colored fruit could be "] 
         stringByAppendingString:fruitName] 
         stringByAppendingString:@"!"]; 


    return result; 
} 


@end 

ответ

1

Где "fruitImage" приходят из в AppDelegate.m? Я не вижу, чтобы это было объявлено.

линия:

__block NSURLSessionDownloadTask *imgTask 

немного странно, потому что вы маркировки imgTask в качестве эталона, который может изменить в блоке, но это также возвращаемое значение. Это может быть частью вашей проблемы, но, по крайней мере, это неясно. Я могу утверждать, что все переменные, которые вы отметили __block, не должны быть такими.

Обычно утечка памяти в этих ситуациях вызвана аспектом захвата переменной блока, но я не вижу очевидного преступника. Шаблон «Слабый я» может помочь вам здесь.

Использование «утечек» может помочь вам понять, какие объекты протекают, что может помочь изолировать то, на что нужно сосредоточиться, а также попытаться взглянуть на жизненные циклы вашего блока. Если блок удерживается объектом, он может создавать циклы, неявно сохраняя другие объекты.

Пожалуйста, обратите внимание, когда вы точно определяете, что происходит.

ссылка: What does the "__block" keyword mean? Always pass weak reference of self into block in ARC?