Convert NSString into NSMutableArray - iphone

I am trying to convert (or copy?) a NSString into a NSMutableArray. I guess my problem is that I don't really understand the structure of a MutableArray. In my limited knowledge, an Array could look like this:
NoteBook = [[NSMutableArray alloc] init];
for (int temp = 0; temp < 3; temp++) {
[NoteBook insertObject:#"Page" atIndex:temp];
}
Which would give me an Array of PagePagePage. Let's assume I wanted to open a txt file which contains PagePagePage, but the words were separated by a defined string so that I can keep the individual objects in my array apart, like so: Page--- end of page ---Page--- end of page ---Page.
Now, my next step would be to read this information from the txt file:
NSString *tempTextOut = [NSString stringWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:&error];
NoteBook = [tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"];
However, the last line does not work and I'm told by xCode: Incompatible Objective-C types assigning 'struct NSArray*', expected 'struct NSMutableArray*'. I don't really understand this - NSArray and MutableArray should be compatible (as one is the subclass of the other). Shouldn't xCode tell me that the problem is that I've been trying to convert a NSString into an NSMutableArray?
Would I perhaps need to re-set my MutableArray before putting something back into it, because right now, it still contains PagePagePage which I have assigned to it in the first step. I thought my NoteBook mutable array would simply be replaced by the string, but I guess that won't be the case.
I'd very much appreciate any help in this matter. Thanks!

componentsSeparatedByString: returns a plain immutable NSArray, not an NSMutableArray. You can pass the array to [NSMutableArray arrayWithArray:] or use mutableCopy on the array to get a mutable array from it, or you can use addObjectsFromArray: on an existing NSMutableArray to add objects to it.
If you go the mutableCopy route, do remember that you are responsible for calling release or autorelease on it.

Assigning a mutable object to the same immutable type will lead to runtime errors if you want to manipulate the immutable instance.
You can get your mutable copy by calling:
NoteBook = [[tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"] mutableCopy];
If NoteBook is a retained property you should assign to it this way:
self.NoteBook = [[[tempTextOut componentsSeparatedByString: #"\n--- end of page ---\n"] mutableCopy] autorelease];
so the mutable copy doesn't get over retained. You can release in your dealloc method then as normal.

Related

Using converted NSNumber to NSString returns EXC_BAD_ACCESS

I have built one 'deep' NSMutableDictionary that contains parsed XML data along with other relevant information that I can pass from one view controller to another. The NSMutableDictionary contains mostly NSStrings but also another NSMutableDictionary more NSStrings and finally followed even deeper with an NSMutableArray of custom objects (it's for a calendar).
Now, because it is a calendar, there are obviously dates involved. The XML data that I receive and parse using NSXMLParser returns strings, so it was on me to convert the day's date to usable numbers. The date in XML comes in with the following format: "MM.DD" I created the following method to do so:
- (void)createDateCodesWithString:(NSString *)string
{
NSInteger monthCode;
NSInteger dayCode;
....
NSArray *dates = [string componentsSeparatedByString:#"."];
monthCode = [[dates objectAtIndex:0] integerValue];
dayCode = [[dates objectAtIndex:1] integerValue];
....
shortDay = [NSNumber numberWithInt:dayCode];
}
'shortDay' is a NSNumber* and an ivar and set as a property (nonatomic, retain) for the custom object that I have created. When I run NSLog commands in the console, it appears that 'shortDay' and other data has been stored successfully in the deep NSMutableDictionary. I run in to problems, however, when I try to access the data again. When I access a NSString* stored ivar, things work OK, but when I attempt to access the NSNumber* I am given the error EXC_BAD_ACCESS with either code 1 or code 2. This is how I try to call upon the NSNumber*
NSNumber *number = day.shortDay;
return [number stringValue];
Might the problem be because the NSArray *dates strips the string into month and day strings and the day string, being two characters long, may contain a '0' before, say, a '6' if the day is the 6th of the month? Any advice?
I am happy to post more code if needed.
It's possible that the memory for shortDay is being cleaned up before the next time you try to access it. When assigning it, try this instead:
shortDay = [[NSNumber numberWithInt:dayCode] retain];
to increase the reference count (AKA take ownership of the object) to avoid the memory being deallocated too early.
If this resolves the problem, you will then need to call [shortDay release] in the dealloc method of your class, such that the memory for it will be properly deallocated at the right time.

NSMutableArray is deleting all object with same string

I am using one NSMutableArray with same string object.
Here is the code
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSObject *obj = [arr objectAtIndex:2];
[arr removeObject:obj];
NSLog(#"%#",arr);
When i try to remove 3rd object of array, its removing all object with "hi" string.
I am not getting why its happening.
my doubt is while removing object, NSMutableArray match string or address.
It's because you're using removeObject which removes all objects that are "equal" to the one you pass in. As per this Apple documentation:
This method uses indexOfObject: to locate matches and then removes
them by using removeObjectAtIndex:. Thus, matches are determined on
the basis of an object’s response to the isEqual: message. If the
array does not contain anObject, the method has no effect (although it
does incur the overhead of searching the contents).
You're seeing the effects of literal strings here where each of those #"hi" objects will turn out to be the same object just added many times.
What you really want to do is this:
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
[arr removeObjectAtIndex:2];
NSLog(#"%#",arr);
Then you're specifically removing the object at index 2.
According to https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html
removeObject:
Removes all occurrences in the array of a given object.
which is exactly the behaviour you're seeing. If you want to remove the object at a particular position, you want removeObjectAtIndex:.
NSMutableArray *arr = [[NSMutableArray alloc]initWithObjects:#"hello",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",#"hi",nil];
NSUInteger obj = [arr indexOfObject:#"hi"]; //Returns the lowest integer of the specified object
[arr removeObjectAtIndex:obj]; //removes the object from the array
NSLog(#"%#",arr);

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.

iPhone - Writing NSMutableDictionary to file

I'm having trouble in writing mutable dictionary to a file. Here's the code that I'm writing.
I'm reading the file like below: (first time when app is ran, it won't have any data)
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
self.favSaveFilePath = [[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:#"Favorites.plist"]];
if([fm fileExistsAtPath:favSaveFilePath])
{
favoriteJokesDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.favSaveFilePath];
favoriteJokes = [[NSMutableArray alloc] initWithArray:[self.favoriteJokesDictionary objectForKey:#"FavoriteJokes"]];
return;
}
I'm adding new dictionary to array as below:
-(BOOL)addJokeToFavorite:(NSDictionary*)joke
{
BOOL success = NO;
[self.favoriteJokes addObject:joke];
success = [self.favoriteJokesDictionary writeToFile:self.favSaveFilePath atomically:YES];
return success;
}
I don't know why its not wring the dictionary to file. Can any one point me the mistake that I'm doing?
The Cocoa API is very specific about what kind of values can legally be in a dictionary when it is written out to file. One particular limitation that has been known to cause problems and which is not thoroughly discussed in the official API documentation is that all of the keys in your dictionary must be of type NSString (and your values must be one of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary), even though the dictionary itself supports keys and values of any object type.
The reason for this restriction has to do with the fact that the dictionary gets stored as an XML document (or "property-list", in Apple-speak) by default. So I'd recommend verifying that your dictionary meets the requirements to be saved to file, and restructuring it if it doesn't.
Or, if restructuring your dictionary is not feasible, you might want to take a look at the NSKeyedArchiver class instead.
Looks like your favoriteJokesDictionary doesn't exist at all when there's no save file to initialize it from. Hence, the attempt to create that save file from the non-existing dictionary in addJokeToFavorite: doesn't do anything, either. You need to create an empty dictionary when there is no save file to begin with.
I was just typing this up when Ulrich posted the correct answer. I'll add the rest here for extra clarification.
As Ulrich pointed out, when you don't have a file, your code just skips the initialization of favoriteJokesDictionary and favoriteJokes. When you try to write out the object later, favoriteJokesDictionary is nil and so the method doesn't do anything.
You can just create an empty NSMutableDictionary if the file doesn't exist yet, and since you're retaining part of the dictionary in a separate ivar as well you should create that at the same time. Something like this would work:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
self.favSaveFilePath = [[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:#"Favorites.plist"]];
if([fm fileExistsAtPath:favSaveFilePath])
{
favoriteJokesDictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.favSaveFilePath];
favoriteJokes = [[NSMutableArray alloc] initWithArray:[self.favoriteJokesDictionary objectForKey:#"FavoriteJokes"]];
} else {
favoriteJokesDictionary = [[NSMutableDictionary alloc] init];
favoriteJokes = [[NSMutableArray alloc] init];
[favoriteJokesDictionary setObject:favoriteJokes forKey:#"JokesArrayKey"];
}
That said, I think you could probably simplify your data model there a bit (unless there much more going on that you're not showing). If you need a dictionary because you're storing more than just jokes, then what you have is fine. However, I wouldn't retain the jokes array, because if you later set a new object in the array for your JokesArrayKey, your favoriteJokes ivar is still going to be pointing to the old array.
I would just grab the object from the dictionary instead whenever you need it:
NSMutableArray *jokes = [favoriteJokesDictionary objectForKey:#"JokesArrayKey"];
Even better, if you are working with the jokes array quite a bit, just pull it out into its own file. You can write an NSArray out as a plist just like you can with an NSDictionary. If you're only using the NSDictionary as a shell to write out a .plist, you can skip it entirely.
One other thing: Assuming your favSaveFilePath property is marked "retain", you have a small memory leak in the first section. You're creating a new retained string with [[NSString alloc] initWithString:...], and then retaining it again by assigning it to your property. You probably just want:
self.favSaveFilePath = [documentsDirectory stringByAppendingPathComponent:#"Favorites.plist"];
Referring to the answer aroth gave, non-ns objects in your dictionary may be more subtle than you realise.
EG: I had an NSArray of NSDictionary objects that I was turning into NSObject subclassed items, and adding these to a NSMutableDictionary.
Even though the subclass only contained NS based objects, strings etc; the NSMutableDictionary itself wouldn't save.
The solution was to save the NSArray of NSDictionary items and turn them into my custom subclass after loading the file.

how do i swap two NSString variables (in a non garbage collected environment)?

I'm trying to swap two strings but I'm not sure if what I am doing is legal (coming from java I'm new to the whole retain count memory management).
Here's my code:
NSString *temp = [[NSString alloc] init];
temp = originalLanguageCode;
originalLanguageCode = translatedLanguageCode;
translatedLanguageCode = temp;
[temp release];
Is this allowed? I'm getting some strange bugs and I'm not sure what's causing them, hoping that this might be it. Any help is very appreciated, thanks!
After assigning a newly allocated string to temp you are immediately assigning originalLanguageCode to it, so the allocated string is completely lost. That is a memory leak. Then you are releasing temp, which was originally originalLanguageCode. This is an over-release.
Try this instead:
NSString *temp = originalLanguageCode;
originalLanguageCode = translatedLanguageCode;
translatedLanguageCode = temp;
Everything looks fine except that in your first line you're creating a string which you immediately leak on the second. You can just say:
NSString *temp = originalLanguageCode;
...
... and get rid of the [temp release] since you didn't create (and don't own) the object assigned to "temp."
Now you didn't say whether originalLanguageCode and translatedLanguageCode are instance variables. If so, use their accessors (which should themselves be doing the right thing with memory management).