Skip to content

Commit

Permalink
Fixes bug #228449: now the appcast doesn't run the RSS feed fetching …
Browse files Browse the repository at this point in the history
…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
andymatuschak committed May 14, 2008
1 parent ce43721 commit d06b2f5
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 92 deletions.
43 changes: 9 additions & 34 deletions RSS.h
Expand Up @@ -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;
Expand All @@ -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
95 changes: 57 additions & 38 deletions RSS.m
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 {

Expand All @@ -184,7 +203,7 @@ - (NSString *) version {


- (void) dealloc {
[incrementalData release];
[headerItems release];

[newsItems release];
Expand Down
38 changes: 18 additions & 20 deletions SUAppcast.m
Expand Up @@ -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
{
Expand All @@ -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;
Expand All @@ -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
Expand Down

0 comments on commit d06b2f5

Please sign in to comment.