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