我已經構建了一個小的演示應用程序,它允許用戶選擇一種顏色,它發送到一個基本的(現在爲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