setValue points to the object instead of copying the NSString - iphone

I'm parsing an XML document. I have an NSMutableString to hold the value for each node I'm passing through and for specific nodes - I want to update the NewsElement class.
This is my code:
[newsElement setValue:currentElementValue forKey:elementName];
The currentElementValue is the NSMutableString the the elementName is key I want to update. Each field is NSString.
The problem is that instead of copying the string, now my NSString points to the currentElementValue address so all of the fields are always the same...

Presumably copying currentElementValue would fix your problem. Have you tried:
[newsElement setValue:[currentElementValue copy] forKey:elementName];
This will make an immutable copy (i.e. an NSString) of the currentElementValue which is an NSMutableString. If you need the copy to be mutable you could use the mutableCopy method instead, for example:
[newsElement setValue:[currentElementValue mutableCopy] forKey:elementName];

Related

App gets gets crash on release on array object in xcode

I am facing strange issue, which is very common and may float at many sites, but this is bit strange. I am allocating mutable array object use object and then release as i did and doing every time of allocating and releasing object. It works fine when i comment release line and just use nil. Below is my code please see and suggest me better way.
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSMutableArray * array = [[NSMutableArray alloc] init];
array= [[dbSingleton sharedInstance] getAll_Players];
NSMutableDictionary * dict = [array objectAtIndex:row];
NSString * autoID = [dict objectForKey:#"autoId"];
NSLog(#"%#",[NSString stringWithFormat:#"%# %#",[dict valueForKey:#"fName"],[dict valueForKey:#"lName"]]);
[array release];
}
Please do not decrease my point because this is very common and people can't like these question to b ask. Thanks in advance.
No need to alloc-init and release your array.
I guess you do not require a new array with the same content, whereas only reference of your sharedInstance array is required.
For that, remove those lines and only decalre your array:
NSMutableArray *array = [[dbSingleton sharedInstance] getAll_Players];
As you have not alloc-init any array here, no need to release the same. Hence, no memory concerns required.
For the max, to reduce reference count what you can do is, immediately when its done with the reference, nullify that one:
NSMutableArray * array= [[dbSingleton sharedInstance] getAll_Players];
NSMutableDictionary * dict = [array objectAtIndex:row];
NSString * autoID = [dict objectForKey:#"autoId"];
NSLog(#"%#",[NSString stringWithFormat:#"%# %#",[dict valueForKey:#"fName"],[dict valueForKey:#"lName"]]);
array = nil; // not mandatory, it will work without this line as well
This methodology will work for both, ARC or Non-ARC.
Hope this helps.
First you initialize the array variable
NSMutableArray * array = [[NSMutableArray alloc] init];
And correctly at this point, you should release this variable at some point.
But, when you write
array= [[dbSingleton sharedInstance] getAll_Players];
You overwrite the array variable you just allocated, and array is now not something you should release here, unless you also call retain on the array you get from getAll_Players.
To fix the issue you should do it like this:
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
NSMutableArray * array = [[dbSingleton sharedInstance] getAll_Players];
NSMutableDictionary * dict = [array objectAtIndex:row];
NSString * autoID = [dict objectForKey:#"autoId"];
NSLog(#"%#",[NSString stringWithFormat:#"%# %#",[dict valueForKey:#"fName"],[dict valueForKey:#"lName"]]);
}
EDIT: (As answer to the question in the comment)
NSMutableArray * array = [[dbSingleton sharedInstance] getAll_Players];
The above line simply sets the variable 'array' as a pointer to whatever '[[dbSingleton sharedInstance] getAll_Players]' returns. The reference count is not increased nor decreased in that line, and you should therefore not decrease it either. (And NEVER trust the retain count value of any object, as objects are not always released when you expect them too).
The reference count is only increased when calling alloc, new, copy and mutableCopy when creating an object and calling retain on and object, and only when you yourself have used these keywords, should you ever release or autorelease an object. Note that this keywords increase the RF count. Where release and autorelease decrease the RF count.
It is expected objective-c practice to make sure that any function that returns an object, returns an object with the retain count of 0, unless the function name has one of the above keywords in its name. (and you should of course call 'autorelease' and not 'release' on the object before returning it)
When you use one of those keywords, the receiver should expect an rf count of 1, and that the receiver will make sure to release the object when he or she is done with it.
This is also what you should expect from any built-in functions.
That is why you should not release the object 'array' from the above line.

How to create a NSArray of unknown NSStrings?

For example, I have #"John", #"Peter", ..., #"May" and need to construct NSArray:
[#"John", #"Peter", ..., #"May"]
The number of NSString is unknown and is taking from an import text file. As NSArray does not support appending new element, how can I create NSArray?
Thanks
UPDATE, let me rephrase the question. How can I create the dynamic array paremeter required by the follow function call?
[segmentedAttributes attributesWithTitlesArray:[NSArray arrayWithObjects:#"John", #"Peter", #"May", nil]]
You don't.
You misunderstand the library behavior.
It is true that there is a convenience constructor arrayWithObjects: which is used thus:
NSArray* array=[NSArray arrayWithObjects:#"Low", #"Medium", #"High", nil];
But this does not create an array with nil at the end. This nil is just to signify the end of the variable-length argument list. It just creates an NSArray with three elements, not four with the last one nil.
You just need to create an NSArray containing the required elements, and pass it to the library function. For example:
NSMutableArray*array=[NSMutableArray array];
while(...){
... get a string ...
[array addObject: string];
}
SCSegmentedAttributes*attributes=[SCSegmentedAttributes attributesWithSegmentTitlesArray:array];
should work, without adding a nil or [NSNull null].
You can't store nil in a Foundation collection class. Instead, you can use [NSNull null]. Use an NSMutableArray, then when you want to add your 'nil' object, use [NSNull null].
Note that when you want to see if an object is [NSNull null] later on, that method will return the same instance every time, so you can do a direct point equality test, like this:
for (id anObject in myArray) {
if (anObject == [NSNull null]) {
NSLog(#"object is nil");
}
else {
NSLog(#"object is not nil: %#", anObject);
}
}
create mutable array then just use
NSString *myString = [NSString stringWithFormat:#"ur new string"];
[myArray addObject:myString];
you have to add object type to array when adding new abject in mutable array.
hope this will help

How do I archive an NSArray of NSDictionary (with NSCoding)?

Suppose I am holding data in an array like this
wordList = [[NSMutableArray alloc] init];
while ([rs next]) //Some database return loop
{
wordDict = [[NSMutableDictionary alloc] init];
[wordDict setObject:[NSNumber numberWithInt:[rs intForColumn:#"id"]] forKey:#"id"];
[wordDict setObject:[rs stringForColumn:#"word"] forKey:#"word"];
[wordList addObject: wordDict];
[wordDict release];
wordDict = nil;
}
But I want to store this result (i.e. wordList) in SQLite for later use - I guess using NSCoding. How would I do that?
(Feel free to point out any errors in how stuff is being alloc'ed if there are problems there).
If you don’t insist on serialization using NSCoding, there’s a writeToFile:atomically: method both on NSArray and NSDictionary. This will serialize your object into a property list (*.plist). The only catch is that all the objects in the “tree” to be serialized must be NSString, NSData, NSArray, or NSDictionary (see the documentation). I’m not sure how NSNumber fits in, but with a bit of luck it will be serialized and deserialized too. The inverse method that will turn the file back into a dictionary or an array is called initWithContentsOfFile:.
As for your code, I would just use the [NSMutableDictionary dictionary] convenience method that gets you an autoreleased dictionary. It’s shorter than the usual alloc & init and you save one line for the explicit release.

NSMutableDictionary containing NSMutableArray returns a string

I am trying to store a variety of strings and a NSMutableArray in a NSMutableDictionary. Basically an XML element with various key/values and one subelement with a list.
NSMutableArray *subItem = [[[NSMutableArray alloc] init] autorelease];
for (GDataXMLElement *subElement in [key children]){
[subItem addObject:[[[self runThroughElement:subElement] copy] autorelease]];
}
[item setObject:[[subItem copy] autorelease] forKey:[key name]];
When I check the type:
NSLog(#"Categories type: %#", [[item objectForKey: #"categories"] classForCoder]);
I get:
Categories type: NSArray
But then in the function which deals with the data and stores it in an object it suddenly appears to be a string:
NSLog(#"Categories type: %#", [[obj objectForKey: #"categories"] classForCoder]);
Which results in:
Categories type: NSString
I am at a loss what is going wrong, why do I get a string out of the dictionary and not the array I put into it? Any help would be really appreciated!
(probably need more info, so please let me know what is relevant)
You shouldn't really be using classForCoder. Instead just log [[obj objectForKey:#"categories"] class]. I'd also try logging the straight [obj objectForKey:#"categories"] to see if it does output a string or an array. If it outputs a string then either obj is not item or your dictionary is being modified at some point.
One bit of advice, there isn't any reason to do [[[NSMutableArray alloc] init] autorelease]. Just call [NSMutableArray array]. Does the same thing but with less code. Same goes for many classes, look for convenience class methods to save you having to do alloc/init/autorelease.

Dynamic, localized NSString

I need to build an NSString that resembles the following:
Name: Craig Buchanan
Telephone: 800-555-1212
Email: name#company.com
Where:
each line (e.g. Telephone) is included or excluded based on the value of a UISwitch
the key part of the string (i.e. the part to the left of the ':') is localized
the value part is from a UITextField.
My approach:
NSMutableArray *values = [[NSMutableArray alloc] initWithCapacity:3];
if (self.nameSwitch.isOn)
[values addObject:[NSString stringWithFormat:#"%#: %#", NSLocalizedString(#"Name", #"Name label"), textFieldName.text]];
if (self.telephoneSwitch.isOn)
[values addObject:[NSString stringWithFormat:#"%#: %#", NSLocalizedString(#"Telephone", #"Telephone number label"), textFieldTelephone.text]];
if (self.emailSwitch.isOn)
[values addObject:[NSString stringWithFormat:#"%#: %#", NSLocalizedString(#"Email", #"Email address label"), textFieldEmail.text]];
return [values componentsJoinedByString:#"\r"];
I have a few questions:
is this a decent approach (i'm an objective-c noob)?
i realize that my array is autoreleased, but still i'm concerned about memory usage. should i release the auto-release pool? seems a bit dangerous.
i'm hoping to make the code a bit more dynamic. my initial thought is to create an array of outlet variables, then use the UISwitch's tag to store the key that drives the localization. thoughts?
Thanks for your time,
Craig Buchanan
Your target language might not use colons, so just make calls like this to add the localized lines:
[values addObject:[NSString stringWithFormat:NSLocalizedString(#"Name: %#", #"Name line"), name];
As for the autorelease question, you can make a local autorelease pool:
NSAutoreleasePool *myPool = [[NSAutoreleasePool alloc] init];
// Do stuff.
[myPool release];
Finally, you can use the switch's tag to indicate an array index. If you do that, you won't even need an IBOutlet variable for the switch; you can just use -viewForTag: or the argument to the action method. You can use NSIndexSet to store the switch state if you like. But if you want to be dynamic, you should probably use a table to hold the switches. If you do that, you can use the table row number instead of a tag.