I was just reading an answer to a different question on nsxmlparsing.. and in it the guy was saying you should use self. notation for better memory management... what dose this mean?
I have left this out of initializing my dictionary should I have it in? how dose it help or not help?
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"Row"])
{
ICRow = [[NSMutableDictionary alloc] initWithDictionary:attributeDict];
}
if ([elementName isEqualToString:#"Rows"]) {
self.ICRows = [NSMutableArray arrayWithCapacity:8];
}
}
bar = self.foo;
is just a shorthand notation for
bar = [self foo];
and
self.foo = bar;
is shorthand for
[self setFoo:bar];
In terms of memory management this just means that the specific setters and getters are invoked, whereas
foo = bar
would simply assign the value (i.e. mostly just copy the pointer).
Say you have a retained property
#property (nonatomic, retain) FooType *foo;
//...
#synthesize foo;
then the setter might look like
- (void) setFoo:(FooType*)x
{
[x retain];
[foo release];
foo = x;
}
Now if you do a
FooType *newFoo = [[FooType alloc] init];
[self setFoo:newFoo];
// or self.foo = newFoo
Everything would be allright, since your previously allocated object is released and the new object is retained properly. If you instead just did a
foo = newFoo;
without thinking about to release the old and retain the new object, you would be leaking memory or worse...
Related
I have a custom class and I set specific properties of that class based on the xml node name coming in.
if([elementName isEqualToString:#"strap"])
{
parcelObj.strap = nodeContent;
}
else if([elementName isEqualToString:#"owner"])
{
parcelObj.owner=nodeContent;
}
etc. etc...
I wanna know if I can lose the else if's all together and do this (somehow)
parcelObj."elementName" = nodeContent;
Make sense? I am not sure how to pass the "elementName" as the property ya know?
This may be a bit more info then you are looking for, but check out my custom xml parser JHXMLParser class. It parses an xml file and creates an array of dictionaries. Each dictionary has the xml tags name as a key for the text value inside the tag. So, if the xml data looked like this:
<PHOTOS>
<PHOTO>
<ID>177</ID>
<THUMB><![CDATA[http://www.blah.com/thumbs/489650795.jpg]]></THUMB>
<IMAGE><![CDATA[http://www.blah.com/images/489650795.jpg]]></IMAGE>
</PHOTO>
<PHOTO>
<ID>178</ID>
<THUMB><![CDATA[http://www.blah.com/thumbs/489650798.jpg]]></THUMB>
<IMAGE><![CDATA[http://www.blah.com/images/489650798.jpg]]></IMAGE>
</PHOTO>
</PHOTOS>
It would create an array with 2 dictionary elements. The first dictionary would have 3 keys ("ID", "THUMB", "IMAGE") and 3 values ("117", "http://www.blah.com/thumbs/489650795.jpg", and "http://www.blah.com/images/489650795.jpg") respectively.
Here are the important methods (_key, _previousTag, _currentTag, _elementText, and _parsedData are ivars and _key is a string that would be #"PHOTO" for the above XML):
- (void)parserDidStartDocument:(NSXMLParser *)parser {
_parsedData = [[NSMutableArray alloc] init];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
_currentTag = [[NSString alloc] initWithString:elementName];
if ([elementName isEqualToString:_key]) {
NSMutableDictionary *tmpDict = [[NSMutableDictionary alloc] init];
[_parsedData addObject:tmpDict];
[tmpDict release];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if ([_previousTag isEqualToString:_currentTag]) {
[_elementText appendString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
} else {
_elementText = [[NSMutableString alloc] initWithString:[string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];
}
if (![_previousTag isEqualToString:_currentTag]) {
_previousTag = [[NSString alloc] initWithString:_currentTag];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
if ([_previousTag isEqualToString:elementName]) {
[[_parsedData lastObject] setObject:_elementText forKey:elementName];
} else {
_previousTag = [[NSString alloc] initWithFormat:#""];
}
}
All,
Disclaimer: I'm aware that there are other questions that ask similar questions, but none provide an answer that I understand or an answer that applies to my situation.
I have two classes, classA and classB. In classA there is a void instance method that creates a NSMutableDictionary (if you want specifics, it's a XMLparser). After classA's XMLParser is run and the NSMutableDictionary has been created and filled, classB is called which needs to do some other things with that dictionary. For some reason, classB cannot access the NSMutableDictionary in classA (actually, it can access it, but for some reason it shows up as "NULL"). What should I do?
Thanks in advance.
EDIT: You asked for the source code, you got it. ClassA below, dictionary in question is called "response."
#import "XMLParser.h"
#import "CardSetupViewController.h"
#implementation XMLParser
#synthesize response;
- (XMLParser *) initXMLParser
{
self = [super init];
// init dictionary of response data
response = [[NSMutableDictionary alloc] init];
return self;
}
//Gets Start Element of SessionData
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qualifiedName
attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:#"SessionData"])
{
NSLog(#"Found SessionData in the return XML! Continuing...");
//response is a NSMutableArray instance variable
//THIS SHOULD NEVER NEED TO BE USED
if (!response)//if array is empty, it makes it!
{
NSLog(#"Dictionary is empty for some reason, creating...");
response = [[NSMutableDictionary alloc] init];
}
//END: THIS SHOULD NEVER BE USED
return;
}
else
{
currentElementName = elementName;
NSLog(#"Current Element Name = %#", currentElementName);
return;
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (!currentElementValue) {
// init the ad hoc string with the value
currentElementValue = [[NSMutableString alloc] initWithString:string];
} else {
[currentElementValue setString:string];
NSLog(#"Processing value for : %#", string);
}
}
//Gets End Element of SessionData
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:#"SessionData"])
{
// We reached the end of the XML document
// dumps dictionary into log
NSLog(#"Dump:%#", [response description]);
return;
}
else
{
//Adds key and object to dictionary
[response setObject:currentElementValue forKey:currentElementName];
NSLog(#"Set values, going around again... brb.");
}
currentElementValue = nil;
currentElementName = nil;
}
#end
You probably want to look into using singletons.
http://www.galloway.me.uk/tutorials/singleton-classes/
http://pixeleap.com/?p=19
http://www.iphonedevsdk.com/forum/iphone-sdk-development/5302-how-make-global-variables-objectivec.html
Those should get you started, if you need clarification just ask.
Are currentElementName and currentElementValue ivars ? If so, every time you catch either an element or a value, you must then release your ivar and retain the new value you catch.
Also, as the comments are saying you didn't follow any rules about writing a correct initializer.
What about classB ? Isn't there some logic that makes your dictionary be released ?
Try managing a bit more the memory.
you have several options... so many options actually
Option A:(set)
#interface ClassB{
NSDictionary * someDict;
}
-(void)setSomeDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(void)setSomeDict:(NSDictionary *)aDict
{
someDict = [aDict retain];//or copy depending on your needs;
}
#end
somewhere in ClassA...
ClassB * b = [ClassB new];
[b setSomeDict: someOtherDict];
Option B:(push)
#interface ClassB{
NSDictionary * someDict;
}
-(void)doSomethingWithDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(void)doSomethingWithDict:(NSDictionary *)aDict
{
NSLog(#"did something with aDict: %#",aDict);
}
#end
somewhere in ClassA...
ClassB * b = [ClassB new];
[b doSomethingWithDict: someOtherDict];
Option c:(init)
#interface ClassB{
NSDictionary * someDict;
}
-(id)initWithDict:(NSDictionary *)aDict;
#end
#implementation ClassB
-(id)initWithDict:(NSDictionary *)aDict
{
self = [super init];
if(self)
{
someDict = [someDict retain]; //or copy depending on your needs
}
NSLog(#"did something with aDict: %#",aDict);
}
#end
somewhere in ClassA...
ClassB * b = [[ClassB alloc] initWithDict:someOtherDict];
[b doSomethingelse];
you can also use properties etc... there are many many options, but you should understand object ownership as well so you don't end up leaking the dictionary.
-(void)dealloc
{
[someDict release];
}
should be added to the classB.
First of all check if u get the response in this delegate method of NSXMLParser
- (void)parserDidEndDocument:(NSXMLParser *)parser;
and send the response via a delegate (which u should implement in this class) and the delegate method is to implemented in the other class ...Simple !!!
Ok, my problem is that whenever i collect data from the parser into an array where the string contains Swedish ÅÄÖ characters. In my example the
[schemaInfoArray objectAtIndex:3]
is supposed to be #"Lördag" but is saved as #"L" and the
[schemaInfoArray objectAtIndex:4]
contains the rest of the string that gets presented as
#"ördag"
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
tempStrang = string;
[schemaInfoArray insertObject:tempStrang atIndex:uppraknare];
uppraknare++;
}
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ( [elementName isEqualToString:#"schemaInfo"] )
{
}
if ( [elementName isEqualToString:#"modfromtid"] )
{
frommodarbtid = [schemaInfoArray objectAtIndex:0];
}
if ([elementName isEqualToString:#"modtomtid"] )
{
tommodarbtid = [schemaInfoArray objectAtIndex:1];
}
if ([elementName isEqualToString:#"modrast"] )
{
modrast = [schemaInfoArray objectAtIndex:2];
}
if ([elementName isEqualToString:#"benamning"] )
{
benamning = [schemaInfoArray objectAtIndex:3];
}
if ([elementName isEqualToString:#"fromnormarb"] )
{
fromnormarbtid = [schemaInfoArray objectAtIndex:4];
}
if ([elementName isEqualToString:#"tomnormarb"] )
{
tomnormarbtid = [schemaInfoArray objectAtIndex:5];
}
if ([elementName isEqualToString:#"rast"] )
{
normrast = [schemaInfoArray objectAtIndex:6];
}
}
Does anyone have any thoughts about how to actually get #"Lördag" to be saved into ONE index instead of getting split into several indexes? This really destroys the structure of things that is supposed to be presented.
This is a documented design choice from Apple, and has nothing to do with Swedish characters:
Because string may be only part of
the total character content for the
current element, you should append it
to the current accumulation of
characters until the element changes.
So you should do just as they say: use a NSMutableString to accumulate the results, and when the element changes, save the buffer to a permanent, (preferrably) immutable NSString.
As requested, here's an example. It was written without any kind of IDE, so chances are that it'll work, but there's no guarantee that it will either compile or work.
#interface Foo : NSObject<NSXMLParserDelegate> {
NSMutableString* accumulator;
NSMutableArray* schemaInfoArray;
int uppraknare; // whatever 'uppraknare' means
}
/* snip */
#end
#implementation Foo
-(void)parser:(NSXMLParser*)parser foundCharacters:(NSString*)string
{
// only accumulate characters, until we get notified that we went through
// the whole XML element
[accumulator appendString:string];
}
-(void)parser:(NSXMLParser*)parser didEndElement:(NSString*)elementName namespaceURI:(NSString*)nsuri qualifiedName:(NSString*)qName
{
// we went through the whole element! time to save!
NSString* immutableResult = [accumulator copy];
[schemaInfoArray insertObject:immutableResult atIndex:uppraknare];
uppraknare++;
[immutableResult release];
// clear the accumulator for the next element
[accumulator deleteCharactersInRange:NSMakeRange(0, [accumulator length])];
/* the rest of your code here */
}
#end
-(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string is not guaranteed to contain the complete contents of the string. You need to have a class instance variable that is a NSMutableString that can append all of foundCharacters between the calls to didStartElement and didEndElement. Inside of didEndElement add the the string to the schemaInfoArray.
OK this should be an easy one but still im breaking my head here:
In my root view controller I have a NSString called "entry" and is working perfectly. I NSLogged it and it works.
I have another class called ´ParseOperation´ and in it i have a NSStringcalled "localEntry" and im trying to send to "ParseOperation" the variable "entry" from "RootViewController" this is my RootViewController code for that:
RootViewController.m
ParseOperation *parseOperation = [[ParseOperation alloc] init];
parseOperation.localEntry = entry;
It just doesn't work. If I NSLog in my ParseOperation.m it returns "null", but if i do it on my RootViewController it returns the correct variable. and yes i did imported the ParseOperation.h
Here is the ParseOperation code (only the part that uses localEntry):
ParseOperation.h
#interface ParseOperation : NSOperation <NSXMLParserDelegate>
{
NSString *localEntry;
}
#property (nonatomic, retain) NSString *localEntry;
#end
ParseOperation.m
#synthesize localEntry;
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
//NSLog(#"entrada %#",localEntry);
if ([elementName isEqualToString:localEntry])
{
self.workingEntry = [[[AppRecord alloc] init] autorelease];
}
storingCharacterData = [elementsToParse containsObject:elementName];
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if (self.workingEntry)
{
if (storingCharacterData)
{
NSString *trimmedString = [workingPropertyString stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[workingPropertyString setString:#""]; // clear the string for next time
if ([elementName isEqualToString:kIDStr])
{
self.workingEntry.appURLString = trimmedString;
}
else if ([elementName isEqualToString:kNameStr])
{
self.workingEntry.appName = trimmedString;
}
else if ([elementName isEqualToString:kImageStr])
{
self.workingEntry.imageURLString = trimmedString;
}
else if ([elementName isEqualToString:kArtistStr])
{
self.workingEntry.artist = trimmedString;
}
}
else if ([elementName isEqualToString:localEntry])
{
[self.workingArray addObject:self.workingEntry];
self.workingEntry = nil;
}
}
}
THANKS!
In all likelihood, rootViewController is nil. When you declare and synthesize a property, it only creates the getter/setter methods for you. It does not initialize the variable to anything.
Since objective-c allows you to message nil, you won't crash when you write:
NSString *localentry = rootViewController.entry;
Messaging nil just returns nil. So if rootViewController is nil, then localentry will be nil as well.
Make sure you're actually setting rootViewController for this class. For example,
ParseOperation *myOperation = [[ParseOperation alloc] init];
[myOperation setRootViewController:rootViewController];
Or, make sure you've established the outlet connection in Interface Builder. In any case, I'd suspect rootViewController is nil. (You can test this with NSLog statements).
Are you sure, since it is an IBOutlet, that you connected to it in interface builder?
To answer my own question I just had to connect the viewController and the ParseOperation programmatically by adding the following to my header in the parseOperation:
#class RootViewController;
RootViewController *rootViewController;
#property (nonatomic, retain) IBOutlet RootViewController *rootViewController;
And the following on the m file of the ParseOperation:
#import "RootViewController.h"
rootViewController = [[RootViewController alloc]init];
After that in the parse operation I just declared:
localEntry= rootViewContoller.entry;
Hey,
I have to parse XML in my iOS app. I took Apple's SeismicXML Sample as my base, but I'm experiencing a really strange behaviour.
These are my parser methodes:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict {
if ([elementName isEqualToString:kEntryElementName]) {
Photo *photo = [[Photo alloc] init];
self.currentPhotoObject = photo;
[photo release];
} else if ([elementName isEqualToString:kTitleElementName] ||
[elementName isEqualToString:kLocationElementName] ||
[elementName isEqualToString:kAuthorElementName]) {
accumulatingParsedCharacterData = YES;
[currentParsedCharacterData setString:#""];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName {
if ([elementName isEqualToString:kEntryElementName]) {
NSLog(#"Did End - Titel:%#", self.currentPhotoObject.titleText);
NSLog(#"Did End - Location:%#", self.currentPhotoObject.locationText);
NSLog(#"Did End - Author:%#", self.currentPhotoObject.author);
[self.currentParseBatch addObject:self.currentPhotoObject];
parsedPhotosCounter++;
if ([self.currentParseBatch count] >= kMaximumNumberOfPhotosToParse) {
[self performSelectorOnMainThread:#selector(addPhotosToList:)
withObject:self.currentParseBatch
waitUntilDone:NO];
self.currentParseBatch = [NSMutableArray array];
}
}
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
else if ([elementName isEqualToString:kLocationElementName]) {
self.currentPhotoObject.locationText = self.currentParsedCharacterData;
}
accumulatingParsedCharacterData = NO;
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (accumulatingParsedCharacterData) {
// If the current element is one whose content we care about, append 'string'
// to the property that holds the content of the current element.
//
[self.currentParsedCharacterData appendString:string];
}
}
Everything works great, the XML Data comes correctly. The parser parses everything as it should.
The problem is in the parser didEndElement methode.
else if ([elementName isEqualToString:kTitleElementName]) {
self.currentPhotoObject.titleText = self.currentParsedCharacterData;
}
When I get "self.currentPhotoObject.titleText" via NSLog, I get the right parsed Data. But then:
else if ([elementName isEqualToString:kAuthorElementName]) {
self.currentPhotoObject.author = self.currentParsedCharacterData;
}
When I get the NSLog of "self.currentPhotoObject.titleText" and from "self.currentPhotoObject.author" here, both give me the author.
In the third parsed methode it is the same. All three properties (titleText, author and locationText) are the locationText.
I have no idea why .titleText e.g. is changed when the parser sets .author.
I have doublechecked everything at least 10 times and compared it to the SeismicXML sample but I can't find the problem.
Please help me. I'm thankfull for every hint !
Greets Sebastian
ps: My properties in the .m file:
#interface ParseOperation () <NSXMLParserDelegate>
#property (nonatomic, retain) Photo *currentPhotoObject;
#property (nonatomic, retain) NSMutableArray *currentParseBatch;
#property (nonatomic, retain) NSMutableString *currentParsedCharacterData;
#end
#implementation ParseOperation
#synthesize photoData, currentPhotoObject, currentParsedCharacterData, currentParseBatch;
It's because you assign same NSMutableString instance to all this properties.
1) Declare author, titleText, locationText properties as copy to avoid this in future.
2) Make a copy each time you want to return value of NSMutableString or assign it to something
self.currentPhotoObject.titleText = [[self.currentParsedCharacterData copy] autorelease];