PDA

View Full Version : Can't access NSString from XML?




ArtOfWarfare
Jul 21, 2011, 01:45 PM
I'm making a side scrolling game. When a player enters a rectangle, I would like it to trigger displaying a message.

At the start of the program, I read an XML file with this parser:

-(void)parseXMLFileAtURL:(NSURL *)file
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:file];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];

[parser release];
}

-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"messageTrigger"])
{
float x = [[attributeDict valueForKey:@"x"] floatValue];
float y = [[attributeDict valueForKey:@"y"] floatValue];
float w = [[attributeDict valueForKey:@"w"] floatValue];
float h = [[attributeDict valueForKey:@"h"] floatValue];

messageTriggers[messageCount] = CGRectMake(x, y, w, h);
messages[messageCount] = [attributeDict valueForKey:@"message"];
messageCount++;
}
}

The XML file looks like this:

<adalez map="test" author="xxx">
<!-- Messages -->
<messageTrigger x="480" y="300" w="480" h="100" message="You are on a hidden platform!"/>
</adalez>

Every time the character moves it checks whether their frame intersects with the message trigger frame.

int i;
for (i = 0; i < messageCount; i++)
{
if (CGRectIntersectsRect(frame, messageTriggers[i]))
{
NSLog(@"Detected that you set off the message trigger.");
NSLog(@"The message is: %@", messages[i]);
messageLabel.text = messages[i];
messageLabel.alpha += 0.05;
break;
}

else messageLabel.alpha -= 0.01;
}

It logs that I set off the message trigger, but it crashes before logging what the message is. It tells me that "Thread 1:Program received signal: "EXC_BAD_ACCESS"" If I take out the log and setting the messageLable.text, it runs fine and properly shows and hides the UILabel.

Edit: In the parser I can get an NSLog that says what [attributeDict valueForKey:@"message"] is, and I can also get an NSLog to give me what messages[messageCount] is right afterwards. Both work fine and display the correct message.



chown33
Jul 21, 2011, 02:05 PM
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"messageTrigger"])
{
float x = [[attributeDict valueForKey:@"x"] floatValue];
float y = [[attributeDict valueForKey:@"y"] floatValue];
float w = [[attributeDict valueForKey:@"w"] floatValue];
float h = [[attributeDict valueForKey:@"h"] floatValue];

messageTriggers[messageCount] = CGRectMake(x, y, w, h);
messages[messageCount] = [attributeDict valueForKey:@"message"];
messageCount++;
}
}

The red-hilited code is doing nothing to claim ownership of the returned value.
Review the Memory Management Guide (http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/MemoryMgmt/).

If you haven't run Build and Analyze, you should.

If you're not testing in Instruments with zombies enabled, you should.

ArtOfWarfare
Jul 21, 2011, 02:18 PM
Thank you :)

I can't believe I forgot to retain, now I just feel silly.

Thanks for pointing out that those tools are available.

covertsurfer
Jul 22, 2011, 06:22 AM
Can you show your solution?

I am looking at this and dont understand why you need a retain because surely the dictionary's key should hold that value

chown33
Jul 22, 2011, 10:41 AM
Can you show your solution?

I am looking at this and dont understand why you need a retain because surely the dictionary's key should hold that value

Do you mean the attributeDict? The one passed to the method?

attributeDict isn't owned by the method, and therefore can go away at some point in the near future, perhaps even very shortly after the method returns. If the dictionary goes away, then what prevents the value from going away? Does the key claim ownership of the value, or does the dictionary?

Please review the Memory Management Guide (http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/MemoryMgmt/). Also see the NSDictionary and NSMutableDictionary class reference docs for details on ownership of keys and values.

covertsurfer
Jul 22, 2011, 11:05 AM
If the dictionary goes away and I'm guessing you mean released, it doesn't matter if the key owns the value because the key will be gone as the dictionary has been released??

If the dictionary goes away, then what prevents the value from going away?

In that scenario if you add a retain and the dictionary is released surely the value is gone?

chown33
Jul 22, 2011, 11:54 AM
If the dictionary goes away and I'm guessing you mean released,

No, "goes away" means dealloc'ed. A release doesn't always cause something to go away (be dealloc'ed). A release only causes a dealloc if there are no remaining ownership claims. The number of ownership claims is indicated by retainCount.

The whole point of having retain, release, and retainCount is so there can be multiple ownership claims for an object. Anyone can claim ownership, and the object won't go away (be dealloc'ed) until all ownership claims are removed. This is fundamental stuff from the Memory Management Guide.


it doesn't matter if the key owns the value because the key will be gone as the dictionary has been released??

It matters because ownership (who owns what) matters.


In that scenario if you add a retain and the dictionary is released surely the value is gone?

Please explain the logic for that conclusion.

Here's the explanation for that conclusion being wrong.

If the dictionary has ownership of a key and a value, and you add a retain on the value (i.e. an ownership claim), what is the total number of ownership claims on the value? 2.

If the dictionary is dealloc'ed, it releases all its ownership claims. What is the remaining number of ownership claims on the value? 1.

Whose ownership claim is it? Yours, the one you made with retain.

If someone else adds another ownership claim (calls retain), does that affect your ownership? No.

All of this is fundamental stuff from the Memory Management Guide.

covertsurfer
Jul 22, 2011, 12:26 PM
Ok thanks for the explanation. I think I have it.

I also believe now why the original poster was having problems because the dictionary was getting passed in and being modified but the values were not stored because of the missing retain. In C# we call this reference parameters: if you pass something in, modify it and expect the value to be there. You have to specify a keyword "ref" if you want that functionality to work. I see in this scenario you would use the retain keyword.

chown33
Jul 22, 2011, 01:12 PM
Ok thanks for the explanation. I think I have it.

I also believe now why the original poster was having problems because the dictionary was getting passed in and being modified but the values were not stored because of the missing retain. In C# we call this reference parameters: if you pass something in, modify it and expect the value to be there. You have to specify a keyword "ref" if you want that functionality to work. I see in this scenario you would use the retain keyword.

The dictionary was not being modified. An NSDictionary is immutable by nature (see the class reference doc). A mutable dictionary must be an NSMutableDictionary, which is a distinct subclass, but that's not what was passed to the method.

All Objective-C objects are passed by reference. Always. There is no way to use objects any other way. If you want a copy, you have to ask for a copy (there's a protocol for this: NSCopying). If you want a mutable copy, you have to ask for that: see NSMutableCopying.

C# is garbage-collected, so drawing analogies between it and reference-counting is dubious. For example, with GC enabled (say, in Mac OS X), a retain would be unnecessary (in fact, it would do nothing), and the original code would be correct as-is.

Also, retain is a method, not a keyword. There's a big difference. Methods are overridable (among other things); keywords are not. Method names can be anything; keywords are predefined and fixed.

This has nothing to do with reference vs. non-reference types, nor mutable objects. It's purely a question of ownership and object lifetime. A modifiable (mutable) object retrieved from the dictionary would present exactly the same issues.

covertsurfer
Jul 23, 2011, 05:19 AM
Ok thanks.

Below is a scenario I put together which hopefully makes sense and wondered whether I have it right.

Create new instance of class with dictionary and init it
Populate dictionary
Call method with retain for value
Now there are 2 owners of the value
Call method to release dictionary
Now 1 owner however is that an invalid pointer? Does retain create a
pointer to that value or is that value just retained in memory
separately?
release object that retained value. Now 0 owners?
Release class instance
Dealloc gets automatically called?

Also depending on what the messages object is should you not call copy rather than retain. I read you should call copy on mutable classes/objects and retain if the mutable class has a subclass of mutable ie.NSString and NSMutableString

chown33
Jul 23, 2011, 11:03 AM
Ok thanks.

Below is a scenario I put together which hopefully makes sense and wondered whether I have it right.

Post compilable code for scenarios, not descriptions. Code is unambiguous, even when it's wrong. Descriptions are often ambiguous or unclear.

Create new instance of class with dictionary and init it

What does "class with dictionary" mean? Does it mean an NSDictionary? NSMutableDictionary? Or a class of your own, say ExampleClass, that has a dictionary instance variable?

If it's an ExampleClass of your own, is the dictionary only an ivar, or is it a property? What attributes on the property? If not a property, how are values assigned to ivar?

What methods are used to create the dictionary? alloc and init? Or one of the convenience methods?

Populate dictionary

Is that an NSMutableDictionary? Or did you supply initial keys & objects? Where did the keys and objects come from?

Call method with retain for value

What is the value? How was it created? Who owns it to begin with?


I don't think I need to go on. Post compilable code, and all these questions and ambiguities will be resolved, because we'll see actual code.

When you post compilable code, we can compile it and see what it does. I suggest you do the same before posting it.



Does retain create a
pointer to that value or is that value just retained in memory
separately?

Did you read the Memory Management Guide? Does it say anything about retain "creating a pointer"? I don't even know what you mean by "create a pointer". A pointer is a simple address. Addresses don't have to be "created". They simply have to point to valid memory. Memory at an address needs to be initialized, if it's going to be an object. But that's not "creating a pointer", it's initializing the object's memory. And initializing the object's memory is not at all the same as retain. Please read the Memory Management Guide for what -retain does.


Also depending on what the messages object is should you not call copy rather than retain. I read you should call copy on mutable classes/objects and retain if the mutable class has a subclass of mutable ie.NSString and NSMutableString

That's an entirely different question, covering an entirely different issue.

If the object is being used in a way that mutability is a problem, such as a key in a dictionary, then -copy is proper. If mutability isn't a problem, such as a value in a dictionary, then what's proper depends on what the program intends to do with the value. For example, NSDictionary will -copy keys, but not values. Read its class reference doc, where it says this.