From 0296489fd589a0fc224ff51b4e6e57d695760b3e Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Mon, 16 May 2011 18:10:23 -0600 Subject: [PATCH 01/11] Your puny ObjC memory management is no match for the call to xmlFreeDoc() in GDataXMLDocument's dealloc. With the result that releasing "document" in -feedDownloadSuccessful: means that there's no more data for the table view to display by the time it tries to look through its items. --- AssetManagerTest/Classes/AppDelegate.m | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/AssetManagerTest/Classes/AppDelegate.m b/AssetManagerTest/Classes/AppDelegate.m index 45cba37..d5eac34 100644 --- a/AssetManagerTest/Classes/AppDelegate.m +++ b/AssetManagerTest/Classes/AppDelegate.m @@ -31,11 +31,17 @@ #define kFeedHREF @"http://api.flickr.com/services/feeds/groups_pool.gne?id=1621520@N24&lang=en-us&format=rss_200" +@interface AppDelegate () +@property (readwrite, retain) GDataXMLDocument *document; +@end + @implementation AppDelegate @synthesize assetManager; @synthesize window; +@synthesize document; + - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]]; @@ -69,7 +75,7 @@ - (void)feedDownloadSuccessful:(ZSURLConnectionDelegate*)delegate DLog(@"success!"); NSError *error = nil; - GDataXMLDocument *document = [[GDataXMLDocument alloc] initWithData:[delegate data] options:0 error:&error]; + document = [[GDataXMLDocument alloc] initWithData:[delegate data] options:0 error:&error]; ZAssert(!error || document, @"Error parsing xml: %@", error); DLog(@"document %@", document); @@ -95,7 +101,6 @@ - (void)feedDownloadSuccessful:(ZSURLConnectionDelegate*)delegate id root = [[navController viewControllers] objectAtIndex:0]; [root populateWithXMLItems:items]; - MCRelease(document); } - (void)feedDownloadFailure:(ZSURLConnectionDelegate*)error @@ -103,4 +108,9 @@ - (void)feedDownloadFailure:(ZSURLConnectionDelegate*)error ALog(@"Failure: %@", error); } +- (void)dealloc +{ + MCRelease(document); + [super dealloc]; +} @end From 00db141e6ecc7de33caf1470405e5a5dc31226a3 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Mon, 16 May 2011 18:32:43 -0600 Subject: [PATCH 02/11] Make the table view work, and add thumbnail images. --- AssetManagerTest/Classes/AppDelegate.m | 13 +++++- AssetManagerTest/Classes/RootViewController.h | 6 ++- AssetManagerTest/Classes/RootViewController.m | 42 +++++++++++++++---- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/AssetManagerTest/Classes/AppDelegate.m b/AssetManagerTest/Classes/AppDelegate.m index d5eac34..6bdb2c9 100644 --- a/AssetManagerTest/Classes/AppDelegate.m +++ b/AssetManagerTest/Classes/AppDelegate.m @@ -66,6 +66,7 @@ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(N MCRelease(delegate); assetManager = [[ZSAssetManager alloc] init]; + [root setAssetManager:assetManager]; return YES; } @@ -92,6 +93,14 @@ - (void)feedDownloadSuccessful:(ZSURLConnectionDelegate*)delegate NSURL *url = [NSURL URLWithString:urlString]; ZAssert(url, @"Bad url: %@", urlString); [cacheRequest addObject:url]; + + GDataXMLElement *mediaThumbnail = [[item elementsForName:@"media:thumbnail"] lastObject]; + ZAssert(mediaThumbnail, @"Failed to find media thumbnail: %@", item); + + NSString *thumbnailURLString = [[mediaThumbnail attributeForName:@"url"] stringValue]; + NSURL *thumbnailURL = [NSURL URLWithString:thumbnailURLString]; + ZAssert(thumbnailURL, @"Bad URL: %@", thumbnailURLString); + [cacheRequest addObject:thumbnailURL]; } [assetManager queueAssetsForRetrievalFromURLSet:cacheRequest]; @@ -110,7 +119,7 @@ - (void)feedDownloadFailure:(ZSURLConnectionDelegate*)error - (void)dealloc { - MCRelease(document); - [super dealloc]; + MCRelease(document); + [super dealloc]; } @end diff --git a/AssetManagerTest/Classes/RootViewController.h b/AssetManagerTest/Classes/RootViewController.h index 0e6a8a8..209c365 100644 --- a/AssetManagerTest/Classes/RootViewController.h +++ b/AssetManagerTest/Classes/RootViewController.h @@ -26,7 +26,11 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. -@interface RootViewController : UITableViewController +#import "ZSAssetManager.h" + +@interface RootViewController : UITableViewController + +@property (readwrite, retain) ZSAssetManager *assetManager; - (void)populateWithXMLItems:(NSArray*)items; diff --git a/AssetManagerTest/Classes/RootViewController.m b/AssetManagerTest/Classes/RootViewController.m index c59c400..234e563 100644 --- a/AssetManagerTest/Classes/RootViewController.m +++ b/AssetManagerTest/Classes/RootViewController.m @@ -37,10 +37,13 @@ @interface RootViewController() @implementation RootViewController @synthesize xmlItems; +@synthesize assetManager; - (void)viewDidLoad { [super viewDidLoad]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageDownloadComplete:) name:kImageDownloadComplete object:[self assetManager]]; } - (void)populateWithXMLItems:(NSArray*)items @@ -49,6 +52,11 @@ - (void)populateWithXMLItems:(NSArray*)items [[self tableView] reloadData]; } +- (void)imageDownloadComplete:(NSNotification *)notification +{ + [[self tableView] reloadData]; +} + #pragma mark - #pragma mark UITableViewDatasource @@ -65,15 +73,35 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator]; } -// GDataXMLElement *item = [[self xmlItems] objectAtIndex:[indexPath row]]; -// GDataXMLElement *title = [[item elementsForName:@"title"] lastObject]; -// if (!title) { -// [[cell textLabel] setText:@"Untitled"]; -// } else { -// [[cell textLabel] setText:[title stringValue]]; -// } + GDataXMLElement *item = [[self xmlItems] objectAtIndex:[indexPath row]]; + GDataXMLElement *title = [[item elementsForName:@"title"] lastObject]; + if (!title) { + [[cell textLabel] setText:@"Untitled"]; + } else { + [[cell textLabel] setText:[title stringValue]]; + } + + GDataXMLElement *mediaThumbnail = [[item elementsForName:@"media:thumbnail"] lastObject]; + ZAssert(mediaThumbnail, @"Failed to find media thumbnail: %@", item); + + NSString *thumbnailURLString = [[mediaThumbnail attributeForName:@"url"] stringValue]; + NSURL *thumbnailURL = [NSURL URLWithString:thumbnailURLString]; + [[cell imageView] setImage:[assetManager imageForURL:thumbnailURL]]; return cell; } +- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath +{ + [tableView deselectRowAtIndexPath:indexPath animated:NO]; +} + +#pragma mark - +#pragma mark Memory management +- (void)dealloc +{ + [xmlItems release]; + [assetManager release]; + [super dealloc]; +} @end From 4fed0e14efc8a8569a123ace3893416d906ea4ab Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Fri, 20 May 2011 14:52:33 -0600 Subject: [PATCH 03/11] Convert ZSURLConnectionDelegate to save incoming data to a file, the better to keep memory use under control. --- ZSURLConnectionDelegate.h | 2 +- ZSURLConnectionDelegate.m | 59 +++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/ZSURLConnectionDelegate.h b/ZSURLConnectionDelegate.h index 13e0990..e624ee2 100755 --- a/ZSURLConnectionDelegate.h +++ b/ZSURLConnectionDelegate.h @@ -41,7 +41,7 @@ void decrementNetworkActivity(id sender); @property (nonatomic, assign, getter=isVerbose) BOOL verbose; @property (nonatomic, assign, getter=isDone) BOOL done; -@property (nonatomic, readonly) NSMutableData *data; +@property (nonatomic, readonly) NSData *data; @property (nonatomic, retain) id object; @property (nonatomic, retain) NSString *filePath; diff --git a/ZSURLConnectionDelegate.m b/ZSURLConnectionDelegate.m index 02086a2..34be528 100755 --- a/ZSURLConnectionDelegate.m +++ b/ZSURLConnectionDelegate.m @@ -61,6 +61,11 @@ void decrementNetworkActivity(id sender) } } +@interface ZSURLConnectionDelegate () +@property (readwrite, retain) NSString *inProgressFilePath; +@property (readwrite, retain) NSFileHandle *inProgressFileHandle; +@end + @implementation ZSURLConnectionDelegate @synthesize verbose; @@ -81,6 +86,9 @@ @implementation ZSURLConnectionDelegate @synthesize startTime; @synthesize duration; +@synthesize inProgressFilePath; +@synthesize inProgressFileHandle; + static dispatch_queue_t writeQueue; static dispatch_queue_t pngQueue; @@ -109,12 +117,19 @@ - (void)dealloc connection = nil; object = nil; + if (![self filePath]) { + // If no filePath was set, don't litter the temp dir with orphaned downloaded files. + [[NSFileManager defaultManager] removeItemAtPath:[self inProgressFilePath] error:nil]; + } MCRelease(delegate); MCRelease(filePath); MCRelease(myURL); MCRelease(data); MCRelease(response); + MCRelease(inProgressFilePath); + MCRelease(inProgressFileHandle); + [super dealloc]; } @@ -139,6 +154,8 @@ - (void)finish - (void)connectionDidFinishLoading:(NSURLConnection*)connection { + [[self inProgressFileHandle] closeFile]; + DLog(@"finished for %@", [self myURL]); if ([self isCancelled]) { [[self connection] cancel]; @@ -148,28 +165,11 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection [self setDuration:([NSDate timeIntervalSinceReferenceDate] - [self startTime])]; - if (![self filePath]) { - if ([[self delegate] respondsToSelector:[self successSelector]]) { - [[self delegate] performSelectorOnMainThread:[self successSelector] withObject:self waitUntilDone:YES]; - } - [self finish]; - return; + // Even if filePath was set, the delegate might try to look at the data blob. + data = [[NSData alloc] initWithContentsOfMappedFile:[self inProgressFilePath]]; + if ([[self delegate] respondsToSelector:[self successSelector]]) { + [[self delegate] performSelectorOnMainThread:[self successSelector] withObject:self waitUntilDone:YES]; } - - NSData *localizedData = [self data]; - NSString *localizedFilepath = [self filePath]; - - dispatch_sync(writeQueue, ^{ - NSError *error = nil; - ZAssert([localizedData writeToFile:localizedFilepath atomically:NO], @"Failed to write to %@\n%@\n%@", localizedFilepath, [error localizedDescription], [error userInfo]); - - if (![[self delegate] respondsToSelector:[self successSelector]]) return; - - dispatch_sync(dispatch_get_main_queue(), ^{ - [[self delegate] performSelector:[self successSelector] withObject:self]; - }); - }); - [self finish]; } @@ -182,8 +182,15 @@ - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLRes } if ([self isVerbose]) DLog(@"fired"); [self setResponse:resp]; - MCRelease(data); - data = [[NSMutableData alloc] init]; + + if ([self filePath]) { + [self setInProgressFilePath:[self filePath]]; + } else { + [self setInProgressFilePath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%d", abs([[[self myURL] absoluteString] hash])]] retain]]; + } + [[NSFileManager defaultManager] removeItemAtPath:[self inProgressFilePath] error:nil]; + [[NSFileManager defaultManager] createFileAtPath:[self inProgressFilePath] contents:nil attributes:nil]; + [self setInProgressFileHandle:[NSFileHandle fileHandleForWritingAtPath:[self inProgressFilePath]]]; [self setStartTime:[NSDate timeIntervalSinceReferenceDate]]; } @@ -195,11 +202,15 @@ - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)newData return; } if ([self isVerbose]) DLog(@"fired"); - [data appendData:newData]; + dispatch_sync(writeQueue, ^{ + [[self inProgressFileHandle] writeData:newData]; + }); } - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error { + [[self inProgressFileHandle] closeFile]; + if ([self isCancelled]) { [[self connection] cancel]; [self finish]; From ede100b62de164ef431ed44cf208dc900a82b931 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Fri, 20 May 2011 14:55:53 -0600 Subject: [PATCH 04/11] Add an explanatory note to the README --- README.markdown | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.markdown b/README.markdown index c1af3fe..46d19ef 100644 --- a/README.markdown +++ b/README.markdown @@ -2,6 +2,8 @@ In this repository is code that we share between many projects that we work on. It is licensed under the BSD license and is free to use as you wish. +This is a fork of ZDS_Shared with changes to the ZSURLConnectionDelegate to reduce memory usage when downloading large files. + ## Prefix.pch ## You will probably find macros in this code that does not compile. The reason is that we have several macros that we add to our Prefix.pch file upon project creation. Those macros are as follows: From e69a66a7d26a4ebdae55a1127cd8741f67658acf Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Fri, 20 May 2011 15:23:31 -0600 Subject: [PATCH 05/11] Add convenience lookup of HTTP status --- ZSURLConnectionDelegate.h | 1 + ZSURLConnectionDelegate.m | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ZSURLConnectionDelegate.h b/ZSURLConnectionDelegate.h index e624ee2..b7bb299 100755 --- a/ZSURLConnectionDelegate.h +++ b/ZSURLConnectionDelegate.h @@ -47,6 +47,7 @@ void decrementNetworkActivity(id sender); @property (nonatomic, retain) NSString *filePath; @property (nonatomic, retain) NSURL *myURL; @property (nonatomic, retain) NSHTTPURLResponse *response; +@property (readonly) NSInteger HTTPStatus; @property (nonatomic, retain) id delegate; @property (nonatomic, assign) SEL successSelector; diff --git a/ZSURLConnectionDelegate.m b/ZSURLConnectionDelegate.m index 34be528..8de4a0e 100755 --- a/ZSURLConnectionDelegate.m +++ b/ZSURLConnectionDelegate.m @@ -64,6 +64,7 @@ void decrementNetworkActivity(id sender) @interface ZSURLConnectionDelegate () @property (readwrite, retain) NSString *inProgressFilePath; @property (readwrite, retain) NSFileHandle *inProgressFileHandle; +@property (readwrite) NSInteger HTTPStatus; @end @implementation ZSURLConnectionDelegate @@ -77,6 +78,7 @@ @implementation ZSURLConnectionDelegate @synthesize filePath; @synthesize myURL; @synthesize response; +@synthesize HTTPStatus; @synthesize successSelector; @synthesize failureSelector; @@ -182,6 +184,7 @@ - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLRes } if ([self isVerbose]) DLog(@"fired"); [self setResponse:resp]; + [self setHTTPStatus:[resp statusCode]]; if ([self filePath]) { [self setInProgressFilePath:[self filePath]]; From 759aee02f55ded5d3bc2618f965e81f7d7874b30 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Fri, 20 May 2011 17:25:49 -0600 Subject: [PATCH 06/11] Add the option to init with an NSURLRequest so the caller has the option to set headers and POST body. Add support for self-signed HTTPS certificates. Add userInfo parameter. --- ZSURLConnectionDelegate.h | 9 +++++ ZSURLConnectionDelegate.m | 84 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/ZSURLConnectionDelegate.h b/ZSURLConnectionDelegate.h index b7bb299..55317cc 100755 --- a/ZSURLConnectionDelegate.h +++ b/ZSURLConnectionDelegate.h @@ -57,6 +57,15 @@ void decrementNetworkActivity(id sender); @property (nonatomic, assign) NSTimeInterval startTime; @property (nonatomic, assign) NSTimeInterval duration; +@property (nonatomic, assign) BOOL acceptSelfSignedCertificates; +@property (nonatomic, copy) NSArray *acceptSelfSignedCertificatesFromHosts; + +@property (readwrite, retain) id userInfo; + +- (id)initWithRequest:(NSURLRequest *)newRequest delegate:(id)delegate; - (id)initWithURL:(NSURL*)aURL delegate:(id)delegate; ++ (id)operationWithRequest:(NSURLRequest *)newRequest delegate:(id)aDelegate; ++ (id)operationWithURL:(NSURL *)aURL delegate:(id)aDelegate; + @end diff --git a/ZSURLConnectionDelegate.m b/ZSURLConnectionDelegate.m index 8de4a0e..ec780fa 100755 --- a/ZSURLConnectionDelegate.m +++ b/ZSURLConnectionDelegate.m @@ -65,6 +65,7 @@ @interface ZSURLConnectionDelegate () @property (readwrite, retain) NSString *inProgressFilePath; @property (readwrite, retain) NSFileHandle *inProgressFileHandle; @property (readwrite) NSInteger HTTPStatus; +@property (readwrite, retain) NSURLRequest *request; @end @implementation ZSURLConnectionDelegate @@ -79,6 +80,7 @@ @implementation ZSURLConnectionDelegate @synthesize myURL; @synthesize response; @synthesize HTTPStatus; +@synthesize request; @synthesize successSelector; @synthesize failureSelector; @@ -91,17 +93,24 @@ @implementation ZSURLConnectionDelegate @synthesize inProgressFilePath; @synthesize inProgressFileHandle; +@synthesize acceptSelfSignedCertificates; +@synthesize acceptSelfSignedCertificatesFromHosts; + +@synthesize userInfo; + static dispatch_queue_t writeQueue; static dispatch_queue_t pngQueue; -- (id)initWithURL:(NSURL*)aURL delegate:(id)aDelegate; +#pragma mark - +#pragma mark Initializers +- (id)initWithRequest:(NSURLRequest *)newRequest delegate:(id)aDelegate; { - ZAssert(aURL, @"incoming url is nil"); if (!(self = [super init])) return nil; + request = [newRequest retain]; delegate = [aDelegate retain]; - [self setMyURL:aURL]; - + [self setMyURL:[newRequest URL]]; + if (writeQueue == NULL) { writeQueue = dispatch_queue_create("cache write queue", NULL); } @@ -113,6 +122,26 @@ - (id)initWithURL:(NSURL*)aURL delegate:(id)aDelegate; return self; } +- (id)initWithURL:(NSURL*)aURL delegate:(id)aDelegate; +{ + ZAssert(aURL, @"incoming url is nil"); + return [self initWithRequest:[NSURLRequest requestWithURL:aURL] delegate:aDelegate]; +} + +#pragma mark - +#pragma mark Convenience factory methods ++ (id)operationWithRequest:(NSURLRequest *)newRequest delegate:(id)aDelegate +{ + return [[[ZSURLConnectionDelegate alloc] initWithRequest:newRequest delegate:aDelegate] autorelease]; +} + ++ (id)operationWithURL:(NSURL *)aURL delegate:(id)aDelegate +{ + return [[[ZSURLConnectionDelegate alloc] initWithURL:aURL delegate:aDelegate] autorelease]; +} + +#pragma mark - +#pragma mark Memory management - (void)dealloc { if ([self isVerbose]) DLog(@"fired"); @@ -127,7 +156,9 @@ - (void)dealloc MCRelease(filePath); MCRelease(myURL); MCRelease(data); + MCRelease(request); MCRelease(response); + MCRelease(userInfo); MCRelease(inProgressFilePath); MCRelease(inProgressFileHandle); @@ -135,14 +166,35 @@ - (void)dealloc [super dealloc]; } +#pragma mark - +#pragma mark Accessors +- (void)setAcceptSelfSignedCertificates:(BOOL)flag +{ + acceptSelfSignedCertificates = flag; + if (flag) { + // Setting the flag to YES implies trusting self-signed certs from everyone. + [acceptSelfSignedCertificatesFromHosts release]; + acceptSelfSignedCertificatesFromHosts = nil; + } +} + +- (void)setAcceptSelfSignedCertificatesFromHosts:(NSArray *)hosts +{ + [acceptSelfSignedCertificatesFromHosts release]; + acceptSelfSignedCertificatesFromHosts = [hosts copy]; + // Set the flag to NO to turn off accept-from-all behavior, limiting access to only the host list. + [self setAcceptSelfSignedCertificates:NO]; +} + +#pragma mark - +#pragma mark Entry point - (void)main { if ([self isCancelled]) return; incrementNetworkActivity(self); - NSURLRequest *request = [NSURLRequest requestWithURL:[self myURL]]; - [self setConnection:[NSURLConnection connectionWithRequest:request delegate:self]]; + [self setConnection:[NSURLConnection connectionWithRequest:[self request] delegate:self]]; CFRunLoopRun(); @@ -154,6 +206,8 @@ - (void)finish CFRunLoopStop(CFRunLoopGetCurrent()); } +#pragma mark - +#pragma mark NSURLConnection delegate methods - (void)connectionDidFinishLoading:(NSURLConnection*)connection { [[self inProgressFileHandle] closeFile]; @@ -227,4 +281,22 @@ - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error [self finish]; } +- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace { + if (([self acceptSelfSignedCertificates]) || ([[self acceptSelfSignedCertificatesFromHosts] count] > 0)) { + return [[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]; + } else { + return NO; + } +} + +- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge { + if (([self acceptSelfSignedCertificates]) || ([[self acceptSelfSignedCertificatesFromHosts] containsObject:[[challenge protectionSpace] host]])) { + if ([[[challenge protectionSpace] authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust]) { + [[challenge sender] useCredential:[NSURLCredential credentialForTrust:[[challenge protectionSpace] serverTrust]] forAuthenticationChallenge:challenge]; + } else { + [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge]; + } + } +} + @end From 6ae14f3e2928583604a904b469c93e6ab938e332 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Fri, 20 May 2011 17:32:33 -0600 Subject: [PATCH 07/11] Update README --- README.markdown | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index 46d19ef..797679e 100644 --- a/README.markdown +++ b/README.markdown @@ -2,7 +2,14 @@ In this repository is code that we share between many projects that we work on. It is licensed under the BSD license and is free to use as you wish. -This is a fork of ZDS_Shared with changes to the ZSURLConnectionDelegate to reduce memory usage when downloading large files. +## About this fork ## + +This fork makes the following changes to ZSURLConnectionDelegate: + +* Incoming data is saved to a file rather than kept in memory, which is useful if you're expecting a large download. +* It's possible to initialize ZSURLConnectionDelegate with an NSURLRequest instead of an NSURL, which is useful if you need to set custom HTTP headers on the request or want to provide a POST body. +* API has been added to optionally support self-signed HTTPS certificates for all hosts or for specific hosts. +* A userInfo parameter and some convenience constructors have been added. ## Prefix.pch ## From 9e92994aa0e16ae3c3fa8b83f126d5c02782c8b9 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Thu, 2 Jun 2011 16:55:46 -0600 Subject: [PATCH 08/11] Sane-ify use of the writeQueue. Writes are now dispatched asynchronously while the network connection loads, and a dispatch group is used to ensure all writes finish. --- ZSURLConnectionDelegate.m | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ZSURLConnectionDelegate.m b/ZSURLConnectionDelegate.m index ec780fa..a80d6f6 100755 --- a/ZSURLConnectionDelegate.m +++ b/ZSURLConnectionDelegate.m @@ -66,6 +66,7 @@ @interface ZSURLConnectionDelegate () @property (readwrite, retain) NSFileHandle *inProgressFileHandle; @property (readwrite) NSInteger HTTPStatus; @property (readwrite, retain) NSURLRequest *request; +@property (readwrite, assign) dispatch_group_t dispatchFileWriteGroup; @end @implementation ZSURLConnectionDelegate @@ -81,6 +82,7 @@ @implementation ZSURLConnectionDelegate @synthesize response; @synthesize HTTPStatus; @synthesize request; +@synthesize dispatchFileWriteGroup; @synthesize successSelector; @synthesize failureSelector; @@ -119,6 +121,10 @@ - (id)initWithRequest:(NSURLRequest *)newRequest delegate:(id)aDelegate; pngQueue = dispatch_queue_create("png generation queue", NULL); } + if (dispatchFileWriteGroup == NULL) { + dispatchFileWriteGroup = dispatch_group_create(); + } + return self; } @@ -160,6 +166,8 @@ - (void)dealloc MCRelease(response); MCRelease(userInfo); + dispatch_release([self dispatchFileWriteGroup]); + MCRelease(inProgressFilePath); MCRelease(inProgressFileHandle); @@ -210,6 +218,8 @@ - (void)finish #pragma mark NSURLConnection delegate methods - (void)connectionDidFinishLoading:(NSURLConnection*)connection { + dispatch_group_wait([self dispatchFileWriteGroup], DISPATCH_TIME_FOREVER); + [[self inProgressFileHandle] closeFile]; DLog(@"finished for %@", [self myURL]); @@ -259,7 +269,7 @@ - (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)newData return; } if ([self isVerbose]) DLog(@"fired"); - dispatch_sync(writeQueue, ^{ + dispatch_group_async([self dispatchFileWriteGroup], writeQueue, ^{ [[self inProgressFileHandle] writeData:newData]; }); } From d0bc32a6e7d0a9b5eac9c5266f3a32a50faf9e9e Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Tue, 14 Jun 2011 14:52:01 -0600 Subject: [PATCH 09/11] Replace dispatch_group_wait with dispatch_group_notify to avoid blocking while waiting for file writes to finish on another queue. --- ZSURLConnectionDelegate.m | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/ZSURLConnectionDelegate.m b/ZSURLConnectionDelegate.m index a80d6f6..7931a6f 100755 --- a/ZSURLConnectionDelegate.m +++ b/ZSURLConnectionDelegate.m @@ -218,25 +218,26 @@ - (void)finish #pragma mark NSURLConnection delegate methods - (void)connectionDidFinishLoading:(NSURLConnection*)connection { - dispatch_group_wait([self dispatchFileWriteGroup], DISPATCH_TIME_FOREVER); - - [[self inProgressFileHandle] closeFile]; - - DLog(@"finished for %@", [self myURL]); - if ([self isCancelled]) { - [[self connection] cancel]; + // Hold the completion block until all outstanding file writes have finished, as indicated by dispatchFileWriteGroup. + dispatch_group_notify([self dispatchFileWriteGroup], writeQueue, ^{ + [[self inProgressFileHandle] closeFile]; + + DLog(@"finished for %@", [self myURL]); + if ([self isCancelled]) { + [[self connection] cancel]; + [self finish]; + return; + } + + [self setDuration:([NSDate timeIntervalSinceReferenceDate] - [self startTime])]; + + // Even if filePath was set, the delegate might try to look at the data blob. + data = [[NSData alloc] initWithContentsOfMappedFile:[self inProgressFilePath]]; + if ([[self delegate] respondsToSelector:[self successSelector]]) { + [[self delegate] performSelectorOnMainThread:[self successSelector] withObject:self waitUntilDone:YES]; + } [self finish]; - return; - } - - [self setDuration:([NSDate timeIntervalSinceReferenceDate] - [self startTime])]; - - // Even if filePath was set, the delegate might try to look at the data blob. - data = [[NSData alloc] initWithContentsOfMappedFile:[self inProgressFilePath]]; - if ([[self delegate] respondsToSelector:[self successSelector]]) { - [[self delegate] performSelectorOnMainThread:[self successSelector] withObject:self waitUntilDone:YES]; - } - [self finish]; + }); } - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSHTTPURLResponse*)resp From 6fead504bd4498e44583fac2594d486737872a02 Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Sun, 29 Jan 2012 21:57:26 -0700 Subject: [PATCH 10/11] Update compiler to llvm --- AssetManagerTest/AssetManagerTest.xcodeproj/project.pbxproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AssetManagerTest/AssetManagerTest.xcodeproj/project.pbxproj b/AssetManagerTest/AssetManagerTest.xcodeproj/project.pbxproj index 4229f09..869a2e8 100644 --- a/AssetManagerTest/AssetManagerTest.xcodeproj/project.pbxproj +++ b/AssetManagerTest/AssetManagerTest.xcodeproj/project.pbxproj @@ -164,6 +164,7 @@ B67896F613328E390022CA67 /* Project object */ = { isa = PBXProject; attributes = { + LastUpgradeCheck = 0420; ORGANIZATIONNAME = "Zarra Studios LLC"; }; buildConfigurationList = B67896F913328E390022CA67 /* Build configuration list for PBXProject "AssetManagerTest" */; @@ -253,6 +254,7 @@ GCC_DYNAMIC_NO_PIC = NO; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Prefix.pch; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = /usr/include/libxml2; INFOPLIST_FILE = Info.plist; OTHER_LDFLAGS = "-lxml2"; @@ -268,6 +270,7 @@ COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Prefix.pch; + GCC_VERSION = com.apple.compilers.llvm.clang.1_0; HEADER_SEARCH_PATHS = /usr/include/libxml2; INFOPLIST_FILE = Info.plist; OTHER_LDFLAGS = "-lxml2"; From bf6e2d40d83e2747f189a739fee533391004da9b Mon Sep 17 00:00:00 2001 From: Tom Harrington Date: Sun, 29 Jan 2012 21:57:38 -0700 Subject: [PATCH 11/11] Fix warning about struct sockaddr_in --- ZSReachability.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ZSReachability.h b/ZSReachability.h index 4e66ee9..930febf 100644 --- a/ZSReachability.h +++ b/ZSReachability.h @@ -48,6 +48,7 @@ #import #import +#import typedef enum { NotReachable = 0,