Я создал небольшое демо-приложение, которое позволяет пользователю выбирать цвет, который отправляется на базовый (для nowhosthost) узел.js-сервера (используя NSURLSessionDataTask), который использует имя цвета для получения имени фрукта и URL изображения и возвращает простой объект свойства JSON, содержащий два объекта.Утечка памяти при вызове NSURLSession и загрузка изображений в NSImage
Когда приложение получает ответ JSON, оно создает предложение с именем цвета и именем фрукта для отображения в графическом интерфейсе, а затем порождает другой вызов NSURLSession (на этот раз с использованием NSURLSessionDownloadTask), чтобы использовать URL-адрес изображения и загружать изображение из фруктов, которые также отображаются в графическом интерфейсе.
В обеих этих сетевых операциях используется [NSURLSession sharedSession].
Я замечаю, что как вызов JSON, так и более заметно загрузка изображений пропускают значительные объемы памяти. Каждый из них следует той же схеме, используя вложенные блоки:
Инициализировать задачу сеанса, передавая блок в качестве обработчика завершения.
Если я правильно понимаю, блок запускается в отдельном потоке, так как связь в 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