Hi All,

I adapted the sample code of "XMLPerformance" as a basis to start my own app. This is for the iPhone but I think it's a general problem with my understanding of Cocoa Memory Management and/or Threading. Basically I have a VewController with a button on it and when the user clicks the button, it should read some XML data from a URL and display it in a Table View. The main thread spawns a secondary thread to handle the downloading and parsing of the data. The main view controller is called via a delegate when a batch worth of array objects (in this case NSMutableDictionary objects) has been parsed or if there are items objects left in the array when parsing operation has completed. At present I am not doing anything with the result, except assigning the values so I can look at them in the debugger. The problem is that when I look at the items in the array passed to the view controller the data retrieved is not as it should be (contains "invalid" or "out of scope" in the debugger). Sometimes the values are correct, but most of the time they are garbage.

The method called:

- (void)parser:(ParserBase*)theParser didParseItems:(NSArray*) theItemDictionaryArray
{
NSEnumerator* myArraytEnumerator = [theItemDictionaryArray objectEnumerator];
        NSDictionary*           myDictionary;
        NSString*                       myObjectNameString;
        NSString*                       myFactYearString;
        NSString*                       mtFactMonthString;
        NSString*                       myFactDayString;
        NSString*                       myFactSourceDatabaseString;
        NSString*                       myFactTextString;
        static int                      myObjectCount=0;
        
        while (myDictionary = [myArraytEnumerator nextObject])
        {
myObjectNameString = [myDictionary objectForKey:[ParserXML parserObjectFieldName]];
                myFactYearString = [myDictionary objectForKey:kField_FactYear];
                mtFactMonthString = [myDictionary 
objectForKey:kField_FactMonth];
                myFactDayString = [myDictionary objectForKey:kField_FactDay];
myFactSourceDatabaseString = [myDictionary objectForKey:kField_FactSourceDatabase];
                myFactTextString = [myDictionary objectForKey:kField_FactText];
                myObjectCount++;
        }
}
myObjectNameString, myFactYearString, mtFactMonthString, myFactDayString, myFactSourceDatabaseString, myFactTextString are gargage most of the time.

I've checked and the data is downloaded and parsed ok. I also changed the code so the batch size was set to 1, which means that parser: didParseItems is called for every completed object instead of accumulating the objects into a batch. If I do this the data seems ok when I look at it in the debugger.

I'm sure the problem must be related to Memory Management but AFAICT I'm doing everything I should! I am aware that there maybe some leaks in the code (and if you spot them please point them out). I was going to do a memory profile on it once I had got everything working ok and get rid of any leaks then.

I have copied a cut-down version of my app below, if anyone can spot the problem I'd be really grateful. I've been programming for many years on Mac's but have not worked with Cocoa for a long time.

Thanks in advance or any help.

All the Best
Dave

---------------------------------------------------------------
//MainViewController.h

@interface MainViewController : UIViewController  <ParserDelegate>
{
        ParserBase*                     mParserBase;
}

@property (nonatomic, retain) ParserBase*       mParserBase;

- (IBAction)getData:(id) sender;

- (void)didEndParsingItems:(ParserBase*)theParser;
- (void)parser:(ParserBase*)theParser didFailWithError:(NSError*) theError; - (void)parser:(ParserBase*)theParser didParseItems:(NSArray*) theItemDictionaryArray;


@end

---------------------------------------------------------------
//MainViewController.m

@implementation MainViewController

@synthesize mParserBase;

static NSString* kDefaultURL = @"http://www.looktowindward.com/dp/ rs_applet2.php?month=Mar&day=11&dbase=main";

static NSString*        kField_List = @"Container";
static NSString*        kField_Detail = @"Detail";
static NSString*        kField_Year = @"FactYear";
static NSString*        kField_Month = @"FactMonth";
static NSString*        kField_Day = @"FactDay";
static NSString*        kField_SourceDatabase = @"FactSourceDatabase";
static NSString*        kField_Text = @"FactText";


- (IBAction)getData:(id) sender
{
        Class                           myParserClass = nil;
        myParserClass = [ParserXML class];
        self.mParserBase = [[[myParserClass alloc] init] autorelease];
        mParserBase.mParserDelegate = self;
        [mParserBase startWithURL:kDefaultURL ForStructure:kField_List];
}


- (void)dealloc
{
        [mParserBase release];
        [super dealloc];
}

- (void)didEndParsingItems:(ParserBase*)theParser
{
        static int myCount=0;
        
        myCount++;
}

- (void)parser:(ParserBase*)theParser didParseItems:(NSArray*) theItemDictionaryArray
{
NSEnumerator* myArraytEnumerator = [theItemDictionaryArray objectEnumerator];
        NSDictionary*           myDictionary;
        NSString*                       myObjectNameString;
        NSString*                       myFactYearString;
        NSString*                       mtFactMonthString;
        NSString*                       myFactDayString;
        NSString*                       myFactSourceDatabaseString;
        NSString*                       myFactTextString;
        static int                      myObjectCount=0;
        
        while (myDictionary = [myArraytEnumerator nextObject])
        {
myObjectNameString = [myDictionary objectForKey:[ParserXML parserObjectFieldName]];
                myFactYearString = [myDictionary objectForKey:kField_FactYear];
                mtFactMonthString = [myDictionary 
objectForKey:kField_FactMonth];
                myFactDayString = [myDictionary objectForKey:kField_FactDay];
myFactSourceDatabaseString = [myDictionary objectForKey:kField_FactSourceDatabase];
                myFactTextString = [myDictionary objectForKey:kField_FactText];
                myObjectCount++;
        }
}

- (void)parser:(ParserBase*)theParser didFailWithError:(NSError *) theError
{
}

//ParserXML.h
---------------------------------------------------------------------
@interface ParserXML : ParserBase
{
        NSURLConnection*                        mURLConnection;
        NSAutoreleasePool*                      mDownloadAndParsePool;
        NSMutableData*                          mXMLSourceData;
        BOOL                                            mParserDoneFlag;
        int                                                     mParsedCount;
        BOOL                                            
mContainerStructureFoundFlag;
        BOOL                                            
mDetailStructureFoundFlag;
        BOOL                                            mGatherCharactersFlag;
        BOOL                                            mIgnoreCharactersFlag;
        NSMutableString*                        mCurrentValueString;
        NSMutableDictionary*            mParsedDataDictionary;
}

@property (nonatomic, retain) NSMutableString*          mCurrentValueString;
@property (nonatomic, retain) NSMutableData*            mXMLSourceData;
@property (nonatomic, retain) NSURLConnection*          mURLConnection;
@property (nonatomic, retain) NSMutableDictionary* mParsedDataDictionary;
@property (nonatomic, assign) NSAutoreleasePool*        mDownloadAndParsePool;

- (void)downloadAndParse:(void*)theObject;

@end

------------------------------------------------------------------------ -----------
//ParserXML.m
@implementation ParserXML

static const NSUInteger         kAutoreleasePoolPurgeFrequency = 20;
static const NSString*          kXMLObjectName = @"_XMLObjectName";


@synthesize     mParsedDataDictionary;
@synthesize mCurrentValueString;
@synthesize     mXMLSourceData;
@synthesize     mURLConnection;
@synthesize     mDownloadAndParsePool;


- (void)downloadAndParse:(void*)theObject;
{
        mParserDoneFlag = NO;
        mContainerStructureFoundFlag = NO;
        mDetailStructureFoundFlag = NO;
        self.mParsedDataDictionary = nil;
        self.mDownloadAndParsePool = [[NSAutoreleasePool alloc] init];
        self.mXMLSourceData = [NSMutableData data];
        
        [[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:self.mParserSourceURL];
        
mURLConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; [self performSelectorOnMainThread:@selector(downloadStarted) withObject:nil waitUntilDone:NO];
        if (mURLConnection != nil)
        {
                do
                {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
                }
                while(mParserDoneFlag == NO);
        }
        
        self.mURLConnection = nil;
        [mDownloadAndParsePool release];
        self.mDownloadAndParsePool = nil;
}

- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
{
        return nil;
}


- (void)connection:(NSURLConnection *)connection didFailWithError: (NSError *)error
{
        mParserDoneFlag = YES;
[self performSelectorOnMainThread:@selector(parseError:) withObject:error waitUntilDone:NO];
}

- (void)connection:(NSURLConnection *)connection didReceiveData: (NSData *)data
{
        [mXMLSourceData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self performSelectorOnMainThread:@selector(downloadEnded) withObject:nil waitUntilDone:NO];
        
        
        self.mCurrentValueString = [NSMutableString string];
        
NSXMLParser* myParser = [[NSXMLParser alloc] initWithData:mXMLSourceData];
        myParser.delegate = self;
        
        [myParser setShouldProcessNamespaces:NO];
        [myParser setShouldReportNamespacePrefixes:NO];
        [myParser parse];
        
[self performSelectorOnMainThread:@selector(parseEnded) withObject:nil waitUntilDone:NO];
        
        [myParser release];
        self.mCurrentValueString = nil;
        self.mXMLSourceData = nil;
        mParserDoneFlag = YES;
}



- (void) finishedCurrentItem
{
        mParsedCount++;
[self performSelectorOnMainThread:@selector(parsedItem:) withObject:mParsedDataDictionary waitUntilDone:NO];
        self.mParsedDataDictionary = nil;
        
        if ((mParsedCount % kAutoreleasePoolPurgeFrequency) == 0)
        {
                [mDownloadAndParsePool release];
                self.mDownloadAndParsePool = [[NSAutoreleasePool alloc] init];
        }
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *) elementName namespaceURI:(NSString *)namespaceURI qualifiedName: (NSString *) qualifiedName attributes:(NSDictionary *)attributeDict
{
        NSString*               myFieldValue = nil;
        
        if ([elementName isEqualToString:mContainerStructureName])
        {
                mContainerStructureFoundFlag = YES;
                mDetailStructureFoundFlag = NO;
                mGatherCharactersFlag = NO;
                mIgnoreCharactersFlag = NO;
        }
        
        else if (mContainerStructureFoundFlag == YES)
        {
                if (mDetailStructureFoundFlag == NO)
                {
self.mParsedDataDictionary = [[[NSMutableDictionary alloc] init] autorelease];
                        myFieldValue = [[NSString alloc] 
initWithString:elementName];
[self.mParsedDataDictionary setObject:myFieldValue forKey:kXMLObjectName];
                        mDetailStructureFoundFlag = YES;
                }
                
                //**
                //**  We are Interested in All Fields in a Detail Structure
                //**
                else    
                {
                        [mCurrentValueString setString:@""];
                        mGatherCharactersFlag = YES;
                }
        }
}


- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *) elementName namespaceURI:(NSString *)namespaceURI qualifiedName: (NSString *)qName
{
        NSString*               myFieldValue = nil;
        if ([elementName isEqualToString:mContainerStructureName])
        {
                mContainerStructureFoundFlag = NO;
        }
else if ([elementName isEqualToString:[mParsedDataDictionary objectForKey:kXMLObjectName]])
        {
                mDetailStructureFoundFlag = NO;
                [self finishedCurrentItem];
        }
        
        else    
        {       
                myFieldValue = [[NSString alloc] 
initWithString:mCurrentValueString     ];
[self.mParsedDataDictionary setObject:myFieldValue forKey:elementName];
        }
        
        mGatherCharactersFlag = NO;
        mIgnoreCharactersFlag = NO;
}

- (void)parser:(NSXMLParser *)theParser foundCharacters:(NSString *) theString
{
        if (mGatherCharactersFlag == NO)
                return;
        
        if (mIgnoreCharactersFlag == NO)
        {
                if ([theString isEqualToString:@"<"] == YES)
                {
                        mIgnoreCharactersFlag = YES;
                        return;
                }
        }
        else
        {
                if ([theString isEqualToString:@">"] == YES)
                {
                        mIgnoreCharactersFlag = NO;
                }
                return;
        }
                
        [mCurrentValueString appendString:theString];
}


- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *) parseError
{
        NSString*               myErrorString;
        NSInteger               myLineNumber;
        NSInteger               myColumnNumber;
        
        myLineNumber = [parser lineNumber];
        myColumnNumber = [parser columnNumber];
        
myErrorString = [NSString stringWithFormat:@"Error %i,Description: % @, Line: %i, Column: %i",[parseError code],[[parser parserError] localizedDescription],[parser lineNumber],[parser columnNumber]];
}

------------------------------------------------------------------------ ------------------------
//ParserBase.h


typedef enum
        {
                ParserTypeAbstract = -1,
                ParserTypeNSXMLParser = 0,
        } ParserType;

@class ParserBase;
@protocol ParserDelegate <NSObject>

@optional
- (void)didEndParsingItems:(ParserBase*)theParser;
- (void)parser:(ParserBase*) theParser didFailWithError:(NSError *) theError; - (void)parser:(ParserBase*)theParser didParseItems:(NSArray*) theItemDictionaryArray;


@end



@interface ParserBase : NSObject
{
        id                                                              
<ParserDelegate> mParserDelegate;
        NSURL*                                                  
mParserSourceURL;
        NSString*                                               
mContainerStructureName;
        NSMutableArray*                                 mParsedItemsArray;
}

@property (nonatomic, assign) id <ParserDelegate> mParserDelegate;
@property (nonatomic, retain) NSURL*                            
mParserSourceURL;
@property (nonatomic, retain) NSString*                         
mContainerStructureName;
@property (nonatomic, retain) NSMutableArray*           mParsedItemsArray;


+ (NSString *)parserName;
+ (ParserType)parserType;
+ (const NSString*)parserObjectFieldName;

- (void)startWithURL:(NSString*) theURLString ForStructure: (NSString*) theContainerStructureName;

- (void)downloadAndParse:(void*)theObject;

- (void)downloadStarted;
- (void)downloadEnded;
- (void)parseEnded;
- (void)parsedItem:(NSMutableDictionary*) theItemDictionary;
- (void)parseError:(NSError *)error;
@end

------------------------------------------------------------------------ --------------------------
//ParserBase.m

@implementation ParserBase


@synthesize mContainerStructureName;


@synthesize mParserDelegate;
@synthesize mParserSourceURL;
@synthesize mParsedItemsArray;


static NSUInteger kCountForNotification = 10;


- (void)startWithURL:(NSString*) theURLString ForStructure: (NSString*) theContainerStructureName;
 {
 [[NSURLCache sharedURLCache] removeAllCachedResponses];
 self.mParsedItemsArray = [NSMutableArray array];
 self.mParserSourceURL = [NSURL URLWithString:theURLString];
 self.mContainerStructureName = theContainerStructureName;
[NSThread detachNewThreadSelector:@selector(downloadAndParse:) toTarget:self withObject:nil];
 }



 - (void)dealloc
 {
 [mParsedItemsArray release];
 //[mParserSourceURL release];
 //[mContainerStructureName release];
 [super dealloc];
 }

 - (void)downloadAndParse:(void*)theObject;
 {
NSAssert([self isMemberOfClass:[ParserBase class]] == NO, @"downloadAndParse - Object is of abstract base class ParserBase");
 }

 - (void)downloadStarted
 {
NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__); [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
 }

 - (void)downloadEnded
 {
NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__); [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
 }


 - (void)parseEnded
 {
NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);

 if  (self.mParserDelegate != nil)
        {
        if (self.mParsedItemsArray.count > 0)
                {
if ([self.mParserDelegate respondsToSelector:@selector (parser:didParseItems:)])
                        {
                        [self.mParserDelegate parser:self 
didParseItems:mParsedItemsArray];
                        }
                }

 [self.mParsedItemsArray removeAllObjects];
if ([self.mParserDelegate respondsToSelector:@selector (didEndParsingItems:)])
        {
        [self.mParserDelegate  didEndParsingItems:self];
        }
 }
}

  - (void)parsedItem:(NSMutableDictionary*) theItemDictionary
 {
NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__);

 [self.mParsedItemsArray addObject:theItemDictionary];
 if (self.mParsedItemsArray.count > kCountForNotification)
        {
        if (self.mParserDelegate != nil)
                {
if ([self.mParserDelegate respondsToSelector:@selector (parser:didParseItems:)])
                        {
                        [self.mParserDelegate parser:self 
didParseItems:mParsedItemsArray];
                        }
                [self.mParsedItemsArray removeAllObjects];
                }
        }
 }


- (void)parseError:(NSError *)error
 {
NSAssert2([NSThread isMainThread], @"%s at line %d called on secondary thread", __FUNCTION__, __LINE__); if (self.mParserDelegate != nil && [self.mParserDelegate respondsToSelector:@selector(parser:didFailWithError:)])
 {
 [self.mParserDelegate parser:self didFailWithError:error];
 }
 }

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to