URL Download
http://code.google.com/p/mwiphonesdk/source/browse/#svn/trunk/iMADE/PrepTasks/08
I have code at the location at the link above and I am using NSMutableString to append strings as data is downloaded. But I am finding that using appendString does not increase the length of the mutable string. I must be doing something wrong.
And when I am done I need to convert NSMutableString to NSString to return it. I have looked for examples to do this but I do not see any yet.
I am most familiar with Java and C# where there is a StringBuffer/StringBuilder which allows you to append pieces and them simply call toString at the end to get a String. It does not appear to be this easy in Objective-C.
NSString* str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
#pragma mark TODO Confirm this is appending a value to the mutable string
[self.mutableString appendString:str];
NSLog(#"str length: %d, %d", [str length], [self.mutableString length]);
Above is the section of code that calls appendString.
I see that str has a length > 0 but calling appendString does not increase the length of self.mutableString.
What am I doing wrong here?
As for having an NSMutableString* and needing to return an NSString*, the former is a subclass of the latter so anywhere you see an NSString* an NSMutableString* will suffice as-is.
Your code looks OK from what you've posted. The only thing I can think of is that perhaps there isn't any data to speak of when initializing the str variable. In such a case appending an empty string will do nothing to mutableString.
You'll also want to make sure self.mutableString has been properly allocated and initialized. You can send messages to NSObject*s that are nil which may be misleading when [self.mutableString length] returns 0.
I have fixed the problem. I simply was not initializing the NSMutableString value and it was transparently not doing anything.
Before appending the string I put the following code.
if (_mutableString == nil){
_mutableString = [[NSMutableString alloc] init];
}
Thanks everyone for answering. And it is good to know that I can use NSMutableString in place of NSString. (that is too easy) :)
Related
I'v been looking around in the questions here and could not find simple example to point me the difference while I was testing some code of my own to test the differentiation.
From what I understand, in an "immutable" string such as 'NSString', I could not preform any 'NSString' methods to modify the string, such as:
NSString *s = #"cat";
s = [NSString stringWithString:#"blamp"];
NSLog(#"%#", s);
But it does work..
Please try to give me and other newbies out there a very simple example of what won't work and why.
tnx
The statement :
s = [NSString stringWithString:#"blamp"];
actually creates a new memory location for the string "blamp" and the old address of s gets replaced by this new address.
And you get the feel that the same s is updated!!! Actually the pointer now points to some other memory addresss.
String manipulation means changing the same string : as if you try
NSString *s = #"cat";
[s appendString:#"s"];//tries to append to the same. this will through error.
//the above works with NSMutableString.
Firs of all, sorry if my english is not absolutely correct. It's not my native language but I'll try to explain myself the best I can.
I'm having a hard time trying to understand the following issue. Take in account the following code:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
[originalText appendString:#" world!!!"]
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
The output:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello world!!!
I find it understandable. I'm working with pointers so if I change the thing and the end of a pointer, I'm changing that thing for all the pointers pointing to it. On the other hand, if I change the code and do this:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
originalText = newText;
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
Then I get this:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello
Why is that? I suppose the correct way to do this is to use a double indirection and an implementation similar to the one used with NSError mechanism but I want to understand why I'm obtaining this behavior. If I can change the contents and the end of myText pointer from the referenceTest: method in the first example, why can't I change the address of myText from the same method in the second example?
I know I'm missing something trivial but I can't find it and I'd like to understand this to better understand the reasoning behind NSError mechanism.
Thank you!
In the second case you're changing the local copy of that pointer. If you want to repoint it in the calling scope, you'd need to use a pointer to a pointer, i.e.:
- (void)referenceTest:(NSMutableString **)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
*originalText = newText;
}
And call it thusly:
[myTest referenceTest:&myText];
And it is worth noting that stringWithString returns an autoreleased string, which means your function is too.
There's a difference between objects and pointers to objects.
Someone has created an NSMutableString object, which exists somewhere in memory. We don't really care where it is. That someone received an NSMutableString* which points to the NSMutableString object. A copy of that NSMutableString* was given to your method referenceTest. There can be any number of pointers to that NSMutableString object, but there is only one object.
The appendString method changes the NSMutableString object itself.
The stringWithString method creates a new NSMutableString object and returns a pointer to it. So now we have two objects, and newText is a pointer to the second one. When you assign newText to originalText, originalText becomes a pointer to the second NSMutableString object. However, originalText is just the parameter in your method. The pointer that the calling method holds isn't changed by this.
I am trying to use NSScanner is an NSArray enumeration loop. This however fails:
-[NSXMLElement length]: unrecognized selector sent to instance 0x280da30
No, I am not calling length anywhere. It seems like the value of found gets changed before the scanner completes, hence making it blank, causing the length error.
If I remove the loop and hardcode the string (i don't want that, it's just a test) the code below works. (excluding the enumeration of course)
It never gets as far as calling the NSLogs either...
Here's my code:
for (NSString*found in arr)
{
NSLog(#"Found %#",found);
NSString*search = [NSString stringWithString:found];
NSString *separatorString = #"\"";
NSString *container = nil;
NSScanner *aScanner = [NSScanner scannerWithString:search];
[aScanner setScanLocation:0];
[aScanner scanUpToString:#"src=\"" intoString:nil];
[aScanner scanString:#"src=\"" intoString:nil];
[aScanner scanUpToString:separatorString intoString:&container];
NSLog(#"scanned");
NSLog(#"%#",container);
NSImage*previewImage = [[NSImage alloc] initWithContentsOfFile:[relativeDir stringByAppendingPathComponent:container]];
[preview setImage:previewImage];
[previewImage release];
progressed = progressed + 1;
[convertProgress setDoubleValue:[convertProgress doubleValue] + progressed];
}
How can I get this to work?
Edit:
Here's the input:
NSXMLDocument*doc = [[NSXMLDocument alloc] initWithData:[NSData dataWithContentsOfFile:webPath] options:NSXMLDocumentTidyHTML error:nil];
NSArray*arr = [doc nodesForXPath:#"//img[#src]" error:nil];
From the documentation:
The nodesForXPath:error: method
returns an array of NSXMLNode objects
Your array is not full of strings. It is full of various NSXML* objects. They aren't garbage. This is exactly how it is supposed to work.
You may not be calling length, but stringWithString: is probably calling length to figure out how much space to allocate in the new string.
Conversion to a string can mean a lot of things. An XML node may be a standalone tag, it may be some inline CDATA, it may be a tag with various attributes. As well, an XML document is highly structured. It is rarely meaningful to walk through the contents of a document as a straight enumeration of tags/contents without applying some amount of structural meaning.
You are likely going to need to recurse or otherwise walk the tags and interpret their contents appropriately.
The first step would be to remove everything but that initial NSLog() and see what you got. Then figure out how to walk the tree of nodes. Then figure out how to parse 'em appropriately.
Are you sure that your arr contains NSString objects? The fact that NSXMLElement is throwing an error looks fishy to me.
Edit
Yep, looks like the docs say the array from nodesForXPath doesn't contain NSString objects, but NSXMLElement objects.
http://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSXMLNode_Class/Reference/Reference.html#//apple_ref/occ/instm/NSXMLNode/nodesForXPath:error:
I am work on a simple program in which I split a string and a user global, I use the following code for splitting the string.
NSString *GlobleStr;//globale variable
//===============
NSString *xmlParsingResult=#"Apple,iphone";
NSArray *array = [xmlParsingResult componentsSeparatedByString:#","];
NSString *StrResult = [NSString stringWithFormat:#"%#", [array objectAtIndex:0]];
GlobleStr =[NSString stringWithFormat:#"%#",[array objectAtIndex:1]];
NSLog(#"cmd %#",StrResult);
NSLog(#"value%#",GlobleStr);
my code can split the string and o/p is cmd:Apple value:iphone
but my problem is that as soon as I call another xib then my global variable will be empty or nil and the application will crash ( it throws error like Variable is not cfstring).
Any suggestions?
It's because NSString's +stringwithFormat: method returns an autoreleased string. In a local variable this is often what you want to prevent memory leaks (otherwise you have to manually release the string when you're done with it). The problem here is that the string in GlobleStr is getting released by the autorelease pool sometime after you assign it, then when you try to access it in another place you get a crash.
The fix is this: GlobleStr = [[NSString stringWithFormat:#"%#",[array objectAtIndex:1]] retain];
As an aside, you can just do this instead:
GlobleStr = [[array objectAtIndex:1] retain];
I strongly recommend reading Apple's documentation regarding memory management in Cocoa: http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html .
Finally, without seeing your code I can't say for sure, but I'd be curious to know why you're using a global variable for GlobleStr. It's a blanket statement, and there are certainly exceptions, but when programming in Cocoa there's probably a better way to structure your code.
You need to retain your global, otherwise it will be deallocated when the autorelease pool drains:
GlobleStr = [[NSString stringWithFormat:#"%#", [array objectAtIndex:0]] retain];
Remember to release it later on when you're done -- in particular, before assigning any other value to it.
- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)