我已经构建了一个小的演示应用程序,它允许用户选择一种颜色,它发送到一个基本的(现在为localhost)node.js服务器(使用NSURLSessionDataTask ),它使用颜色名称来获取水果名称和图像URL,并返回一个简单的包含两个属性的JSON对象。内存泄漏时,NSURLSession调用和加载映像到NSImage
当应用程序收到JSON响应时,它会在GUI中创建一个包含颜色名称和水果名称的句子,然后产生另一个NSURLSession调用(这次使用NSURLSessionDownloadTask)来使用图像URL并下载图片的果实也显示在GUI中。
这两种网络操作都使用[NSURLSession sharedSession]。
我注意到JSON调用和更明显的图像下载都泄漏了大量的内存。他们每个人都使用嵌套块遵循类似的模式:
初始化会话任务,路过一个块作为完成处理。
如果我理解正确,块在单独的线程上运行,因为默认情况下NSURLSession中的通信是异步的,所以更新GUI必须发生在main中,因此在completeHandler块内调用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