Permalink
Browse files

Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …

…in another thread; it just uses NSURLConnection's asynchronous capabilities (which don't leak memory like the synchronous methods) and some delegate methods.
  • Loading branch information...
1 parent ce43721 commit d06b2f5dad83ecfdd96497b420ded1feecac4ee8 @andymatuschak andymatuschak committed May 14, 2008
Showing with 84 additions and 92 deletions.
  1. +9 −34 RSS.h
  2. +57 −38 RSS.m
  3. +18 −20 SUAppcast.m
View
43 RSS.h
@@ -50,6 +50,8 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMA
@interface RSS : NSObject {
+ NSMutableData *incrementalData;
+ id delegate;
NSDictionary *headerItems;
NSMutableArray *newsItems;
@@ -59,44 +61,17 @@ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMA
BOOL normalize;
}
+- (RSS *)initWithURL:(NSURL *) url userAgent:(NSString*)userAgent delegate:delegate;
+- (BOOL)loadData:(NSData *)rssData normalize:(BOOL)fl;
-/*Public*/
-- (RSS *)initWithURL:(NSURL *) url normalize:(BOOL) fl userAgent:(NSString*)userAgent error:(NSError **)error;
-
-- (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) description;
-
-- (RSS *) initWithData: (NSData *) rssData normalize: (BOOL) fl;
-
-- (NSDictionary *) headerItems;
-
-- (NSMutableArray *) newsItems;
-
-- (NSString *) version;
-
-// AMM's extensions for Sparkle
+- (NSMutableArray *)newsItems;
- (NSDictionary *)newestItem;
+@end
-/*Private*/
-
-- (void) createheaderdictionary: (CFXMLTreeRef) tree;
-
-- (void) createitemsarray: (CFXMLTreeRef) tree;
-
-- (void) setversionstring: (CFXMLTreeRef) tree;
-
-- (void) flattenimagechildren: (CFXMLTreeRef) tree into: (NSMutableDictionary *) dictionary;
-
-- (void) flattensourceattributes: (CFXMLNodeRef) node into: (NSMutableDictionary *) dictionary;
-
-- (CFXMLTreeRef) getchanneltree: (CFXMLTreeRef) tree;
-
-- (CFXMLTreeRef) getnamedtree: (CFXMLTreeRef) currentTree name: (NSString *) name;
-
-- (void) normalizeRSSItem: (NSMutableDictionary *) rssItem;
-
-- (NSString *) getelementvalue: (CFXMLTreeRef) tree;
-
+@interface NSObject (RSSDelegateProtocol)
+- (void)feedDidFinishLoading:(RSS *)feed;
+- (void)feed:(RSS *)feed didFailWithError:(NSError *)error;
@end
#endif
View
95 RSS.m
@@ -49,6 +49,18 @@ - (NSString *)localizedFailureReason;
@end
#endif
+@interface RSS (Private)
+- (void) createheaderdictionary: (CFXMLTreeRef) tree;
+- (void) createitemsarray: (CFXMLTreeRef) tree;
+- (void) setversionstring: (CFXMLTreeRef) tree;
+- (void) flattenimagechildren: (CFXMLTreeRef) tree into: (NSMutableDictionary *) dictionary;
+- (void) flattensourceattributes: (CFXMLNodeRef) node into: (NSMutableDictionary *) dictionary;
+- (CFXMLTreeRef) getchanneltree: (CFXMLTreeRef) tree;
+- (CFXMLTreeRef) getnamedtree: (CFXMLTreeRef) currentTree name: (NSString *) name;
+- (void) normalizeRSSItem: (NSMutableDictionary *) rssItem;
+- (NSString *) getelementvalue: (CFXMLTreeRef) tree;
+@end
+
// This comparator function is used to sort the RSS items by their published date.
NSComparisonResult compareNewsItems(id item1, id item2, void *context)
{
@@ -98,72 +110,79 @@ - (RSS *) initWithTitle: (NSString *) title andDescription: (NSString *) descrip
} /*initWithTitle*/
-- (RSS *) initWithData: (NSData *) rssData normalize: (BOOL) fl {
-
+- (BOOL)loadData:(NSData *)rssData normalize:(BOOL)fl
+{
CFXMLTreeRef tree;
-
flRdf = NO;
-
normalize = fl;
NS_DURING
-
tree = CFXMLTreeCreateFromData (kCFAllocatorDefault, (CFDataRef) rssData,
NULL, kCFXMLParserSkipWhitespace, kCFXMLNodeCurrentVersion);
-
NS_HANDLER
-
tree = nil;
-
NS_ENDHANDLER
- if (tree == nil) {
-
- /*If there was a problem parsing the RSS file,
- raise an exception.*/
-
- [self release];
- return nil;
- } /*if*/
+ if (tree == nil)
+ return NO;
[self createheaderdictionary: tree];
-
[self createitemsarray: tree];
-
[self setversionstring: tree];
- CFRelease (tree);
+ return YES;
+}
+
- return (self);
- } /*initWithData*/
+- (RSS *)initWithURL:(NSURL *)url userAgent:(NSString*)userAgent delegate:del
+{
+ self = [super init];
+ if (self)
+ {
+ delegate = del;
+ NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url cachePolicy: NSURLRequestReloadIgnoringCacheData
+ timeoutInterval: 30.0];
+ if (userAgent)
+ [request setValue: userAgent forHTTPHeaderField: @"User-Agent"];
+
+ incrementalData = [[NSMutableData data] retain];
+ NSURLConnection *connection = [NSURLConnection connectionWithRequest:request delegate:self];
+ CFRetain(connection);
+ }
+ return self;
+}
+- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
+{
+ [incrementalData appendData:data];
+}
-
-
-- (RSS *)initWithURL:(NSURL *)url normalize:(BOOL)fl userAgent:(NSString*)userAgent error:(NSError **)error
+- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
- NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: url cachePolicy: NSURLRequestReloadIgnoringCacheData
- timeoutInterval: 30.0];
- if (userAgent)
- [request setValue: userAgent forHTTPHeaderField: @"User-Agent"];
-
- NSURLResponse *response = nil;
- NSData *rssData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:error];
- if (rssData == nil) { return nil; }
+ CFRelease(connection);
+ NSError *error = nil;
@try
{
- [self initWithData:rssData normalize:fl];
+ if (![self loadData:incrementalData normalize:YES])
+ error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObject:SULocalizedString(@"An error occurred while parsing the update feed.", nil) forKey:NSLocalizedDescriptionKey]];
}
@catch (NSException *parseException)
{
- if (error)
- *error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], NSLocalizedFailureReasonErrorKey, nil]];
- return nil;
+ error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:SULocalizedString(@"An error occurred while parsing the update feed: %@", nil), [parseException reason]] forKey:NSLocalizedFailureReasonErrorKey]];
}
- return self;
+
+ if (error)
+ [delegate feed:self didFailWithError:error];
+ else
+ [delegate feedDidFinishLoading:self];
}
+- (void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error
+{
+ CFRelease(connection);
+ [delegate feed:self didFailWithError:error];
+}
- (NSDictionary *) headerItems {
@@ -184,7 +203,7 @@ - (NSString *) version {
- (void) dealloc {
-
+ [incrementalData release];
[headerItems release];
[newsItems release];
View
@@ -9,12 +9,11 @@
#import "Sparkle.h"
#import "SUAppcast.h"
-@implementation SUAppcast
+@interface SUAppcast (Private)
+- (void)reportError:(NSError *)error;
+@end
-- (void)fetchAppcastFromURL:(NSURL *)url
-{
- [NSThread detachNewThreadSelector:@selector(_fetchAppcastFromURL:) toTarget:self withObject:url];
-}
+@implementation SUAppcast
- (void)dealloc
{
@@ -27,18 +26,14 @@ - (NSArray *)items
return items;
}
-- (void)_fetchAppcastFromURL:(NSURL *)url
+- (void)fetchAppcastFromURL:(NSURL *)url
+{
+ RSS *feed = [[RSS alloc] initWithURL:url userAgent:userAgentString delegate:self];
+ CFRetain(feed); // Manage the RSS feed's memory manually.
+}
+
+- (void)feedDidFinishLoading:(RSS *)feed
{
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- NSError *error = nil;
- RSS *feed = [[RSS alloc] initWithURL:url normalize:YES userAgent:userAgentString error:&error];
- if (!feed)
- {
- [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO];
- return;
- }
-
// Set up all the appcast items:
items = [NSMutableArray array];
id enumerator = [[feed newsItems] objectEnumerator], current;
@@ -51,17 +46,20 @@ - (void)_fetchAppcastFromURL:(NSURL *)url
}
@catch (NSException *parseException)
{
- error = [NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], SUTechnicalErrorInformationKey, nil]];
- [self performSelectorOnMainThread:@selector(reportError:) withObject:error waitUntilDone:NO];
+ [self reportError:[NSError errorWithDomain:SUSparkleErrorDomain code:SUAppcastParseError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:SULocalizedString(@"An error occurred while parsing the update feed.", nil), NSLocalizedDescriptionKey, [parseException reason], SUTechnicalErrorInformationKey, nil]]];
return;
}
items = [[NSArray arrayWithArray:items] retain]; // Make the items list immutable.
if ([delegate respondsToSelector:@selector(appcastDidFinishLoading:)])
[delegate performSelectorOnMainThread:@selector(appcastDidFinishLoading:) withObject:self waitUntilDone:NO];
- [feed release];
- [pool release];
+ CFRelease(feed);
+}
+
+- (void)feed:(RSS *)feed didFailWithError:(NSError *)error
+{
+ [self reportError:error];
}
- (void)reportError:(NSError *)error

0 comments on commit d06b2f5

Please sign in to comment.