Dynamic, localized NSString - iphone

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.

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 add object at specified index in NSMutable array?

How can I add object at specified index?
in my problem
NSMutableArray *substring
contains index and object alternatively
and I need to add it to the another array str according to index I getting from this array.
NSMutableArray *str=[NSMutableArray new];
if ([substrings containsObject:#"Category-Sequence:"])
{
NSString *index=[substrings objectAtIndex:5];
//[substrings objectAtIndex:5]
gives me integer position at which I need to add object in `str` array,
gives 5,4,8,2,7,1 etc
NSString *object=[substrings objectAtIndex:1];
//[substrings objectAtIndex:1] gives object,gives NSString type of object
[str insertObject:object atIndex:(index.intValue)];
}
please suggest some way to achieve it.
Thanks in advance!
Allocate the array first & then try to add objects in it.
NSMutableArray *str = [[NSMutableArray alloc]init];
if ([substrings containsObject:#"Category-Sequence:"])
{
NSString *index=[substrings objectAtIndex:5];
NSString *object=[substrings objectAtIndex:1];
[str insertObject:object atIndex:(index.intValue)];
}
Allocate the NSMutableArray before inserting objects into it:
NSMutableArray *strMutableArray = [[NSMutableArray alloc] init];
(You’ll also need to release it when you’re done if you’re not using ARC.)
Or you could also use a temporary object, if you don’t need to keep strMutableArray:
NSMutableArray *strMutableArray = [NSMutableArray array];
Then you can insert objects into the NSMutableArray.
Be careful with using indexes of and in different arrays, however. There might be a better way to do what you want.

Adding string to an array

i wanted to know how to add strings into an array.
I used the following methods but it is showing null.
1) [arrData addObject:[NSString stringWithString:strlast]];
2) [arrData addObject:strlast];
Thanks in advance
You can't add anything to an NSArray once it's created. You need to use an NSMutableArray if you want to make changes to it.
Update: You may actually have two problems.
Using an NSArray instead of an NSMutableArray when mutability is needed.
Not initializing the array object (either kind). If arrData is nil, you can happily send as many messages as you want to nil. Nothing will happen.
If it is showing null (nil) you need to make sure you set arrData somewhere in your code before trying to addObject:.
arrData = [[NSMutableArray alloc] init];
Also strlast is a string so use your second example, the first example is pointless.
[arrData addObject:strlast];
Did you allocate an array and assign it to arrData?
Try:
NSMutableArray *arrData = [NSMutableArray array];
NSString *string = #"My string";
[arrData addObject:string];
NSLog(#"%#", [arrData objectAtIndex:0]); //logs "My string"
If you're using a non-mutable array, you can also use arrayByAddingObject:
arrData = [arrData arrayByAddingObject: strlast];
but a mutable array is probably a better idea.

Can't get an NSString ivar to accept a value from an array

I am storing some data (some floats, some strings) to a plist and then reading them back. When I read them back, I assign them to some ivars in my view controller. They are just ivars, not properties.
The floats take their values fine and I can NSLog them and see that they are correct. But no matter what I try, I can't get my NSString ivars to take the string values.
Example: array positions 6 and 7 have the strings Portland, OR" and "Pacific Daylight Time" stored in them - read back from the plist into *array.
This:
cityName = [NSString stringWithFormat:#"", [array objectAtIndex:6]];
timeZoneName = [NSString stringWithFormat:#"", [array objectAtIndex:7]];
NSLog(#" >cityName from array = %#, and now the iVar is = %#", [array objectAtIndex:6], cityName);
NSLog(#" >timeZoneName from array = %#, and now the iVar is = %#", [array objectAtIndex:7], timeZoneName);
Results in this:
>cityName from array = Portland, OR, and now the iVar is =
>timeZoneName from array = Pacific Daylight Time, and now the iVar is =
I put a breakpoint at the end of the method where this happens and for both NSString ivars, the little pop up message says, Invalid Summary.
cityName = [NSString stringWithFormat:#"%#", [array objectAtIndex:6]];
The mistake is that you've missed the format specifier. Specifically though, if the objects in the array are already strings, you can just assign them straight to the ivar:
cityName = [array objectAtIndex:6];
Be careful about object ownership though. You don't own the object returned by objectAtIndex: so if you need to use this ivar in many different methods, obtain ownership by sending retain, and relinquish ownership using release. Alternatively if you choose to establish cityName as a retaining or copying property of your class, use the dot-notation or accessor method:
self.cityName = [array objectAtIndex:6];
// or
[self setCityName:[array objectAtIndex:6]];
If the array contains NSString instances there is no need to call +stringWithFormat:. It is a waste of CPU cycles and undoes any potential optimizations related to immutable strings. Furthermore, relying upon the results of description is never the right answer (though it is nigh unavoidable with NSStrings -- this is the one spot where you can rely on the result).
This (fixed):
cityName = [NSString stringWithFormat:#"%#", [array objectAtIndex:6]];
timeZoneName = [NSString stringWithFormat:#"%#", [array objectAtIndex:7]];
Could be:
cityName = [array objectAtIndex:6];
timeZoneName = [array objectAtIndex:7];
Copy it if you need to. As #dreamlax said, make sure you follow the memory management rules.
Use below
cityName = [NSString stringWithFormat:#"%#", [array objectAtIndex:6]];
You require to use %d or %i for integers and %# is used for string objects.

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.