Occasional EXC_BAD_ACCESS on NSUserDefaults access or synchronize - objective-c

In a tab-based app when I switch between some tabs, I sometimes get EXC_BAD_ACCESS. It's not every time but if you flick back and forward a few times it eventually happens.
Defined in the .h:
NSUserDefaults *theData;
I've got this in viewWillAppear and viewWillDisappear:
[theData synchronize];
The line at fault gets called in a function at the viewWillAppear stage:
NSMutableArray *thisArray = [theData objectForKey:#"FriendsArray"];
I'm using NSUserDefaults to store a few dictionaries of data. This is populated by server calls, but there's no need for an internal database due to it being refreshed often. I am open to other ways of storing this data if that would be better.
I have tried a number of things like casting it (NSMutableArray *)[theData objectForKey:#"FriendsArray"]; or using arrayForKey and a number of other things with no improvement.
Any help or tips would be greatly appreciated.

NSMutableArray *thisArray = [theData objectForKey:#"FriendsArray"]; // retain
The way you try to make an array mutable is wrong. Also written like that, as suggested in the comments, you should probably retain that array.
Try init/alloc a new mutable array with objects like this :
NSMutableArray *thisArray =
[[NSMutableArray alloc] initWithArray:[theData objectForKey:#"FriendsArray"]];
Another method, -(id)initWithArray:(NSArray *)array copyItems:(BOOL)flag;, allows you to make a copy of the original objects.

Values in NSUserDefaults can not be mutable. You are trying to cast an immutable object into a mutable one. Try casting it into an NSArray, then cast that back into a mutable array. Should fix your issue.

There's no real reason for using
NSUserDefaults *theData;
Instead use this to recover a mutable array from UserDefaults:
NSMutableArray *thisArray =
[[NSUserDefaults standardUserDefaults] objectForKey:#"FriendsArray"] mutableCopy];
This gives you a RETAINED mutable array.
Note also that you only need to use synchronize when saving to UserDefaults, not when reading. You should synchronize pretty much after every save since your app can crash or be shut down by the iOS without you having a chance to synchronize.

Related

Storing NSMutableArray

I am currently storing an instance of a class in an NSMutableArray by using NSValue with "valueWithPointer". The problem is within this class I have another NSMutableArray, whenever I convert the instance of the class back from NSValue the NSMutableArray contained within it is of length 0, all of its elements have been removed. Does anyone know why this happens and how to solve it?
Any help would be much appreciated.
Unless there's a very good reason that you specifically need NSValue, you should not add a class instance to an NSMutableArray that way. Just do:
[myMutableArray addObject:myClassInstance];
This does all the right things with regards to memory management (and avoids ugly pointer value stuff), and should let you get at your class instance's array objects after retrieving the object. See the NSMutableArray docs for a quick starter on how to use the class properly.

Can't add object to NSMutableDictionary (iOS, Objective-C)

I am trying to add an object to an NSMutableDictionary using the setObject:forKey: function,
but for whatever reason, when I run my app in the iPhone simulator and try to perform the action, the code stops at the line were I am trying to add the object to the dictionary. The line where the program gets stuck is as follows:
[_dict setObject:_tf.text forKey:[NSString stringWithFormat:#"keywords_%i", [_keywordsArray count]]];
Where '_dict' is an NSMutableDictionary object, '_tf' is a UITextField, and '_keywordsArray' is an NSMutableArray.
Overall, I am using the objects (aka keys) from my dictionary to populate an NSMutableArray, which is used to fill a UIPickerView. Initially, I load the dictionary from a .plist file, and I can tell the NSMutableDictionary and the NSMutableArray are both getting populated/filled correctly, and that the UIPickerView is working fine, the only part that isn't working is adding objects to the NSMutableDictionary (when the user presses a button in the UI). Also, I did get an error once that said that the NSMutableArray had a count of 0 (zero), when clearly it didn't.
Any help/suggestions would be greatly appreciated!

Allocating and releasing memory in iPhone Xcode 3.2.5, iOS 4.2.1

I am having a strange issue with tableview showing (null) when another ViewController comes and goes. I traced it down to the place that it reads the data to display to the tableview and found that it read null for data. To debug this, I need to understand how memory management really works for iPhone. I used the following code in my application delegate to create an array of objects. This was from a sample code which I modified for my app. My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
//Initialize the hardware array.
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
self.hardwareArray = tempArray;
[tempArray release];
This type of code seems to be very common for iPhone. The tutorials keeps saying for example, now that you passed the data to the controller you can release it. But my C and C++ experience tells me, if you release the memory it would be gone, and if its address is sitting someplace on the stack, its no good. Is the data being copied to another place rather than address being passed?
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens if one released modelName that is already nil?
-(void) setModelName:(NSString *)newValue {
self.isDirty = YES;
[modelName release];
modelName = [newValue copy];
}
My question is when one creates an array like this, would the [tempArray release] actually release the array that was just created?
If the #property of the object is assign it would get released. But usually you use retain or copy as #property. And if the property is retain, the setter you call with self.hardwareArray = ... (ie [self setHardwareArray:...]) will retain the object. If the property is copy it will create a new object that is retained already.
Also when releasing memory in a set routine like the one below, should I be checking to see if the modleName is not nil before releasing it. What happens is one released modelName that is already nil?
In objective-c messages sent to nil are perfectly legal. No need to worry, no need to check for nil.
If you release an instance variable outside of dealloc you should set it to nil after releasing it.
release doesn't do what you think - it isn't equivalent to free() or delete and doesn't cause the memory to be gone.
It really is important to fully understand the few simple memory management rules before you do any coding. It's not difficult, the best resources are the Apple Developer Guides, like this one
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html%23//apple_ref/doc/uid/10000011i

Do I need to use NSKeyedArchiver?

I have an object. It has only two NSStrings as properties.
I created an array to hold several of these objects.
Do I need to use NSKeyedArchiver to save this array to disk, or can I just use NSUserDefaults?
If your object is a custom class, you will need to use NSKeyedArchiver before you put the contents in NSUserDefaults. See here.
You can just use [[NSUserDefaults standardUserDefaults] setObject:myArray forKey:myKey]; for writing and [[NSUserDefaults standardUserDefaults] arrayForKey:myKey]; for reading (there is no setArray:forKey: as it's not needed).
Also, if you got an array that you don't want to store inside NSUserDefaults there's an easy possibility to write and read arrays: -[NSArray writeToFile:atomically:] and +[NSArray arrayWithContentsOfFile:]. There also the same methods for NSDictionary to easily read/write dictionaries. Of course you're always free to use NSKeyed(Un)Archiver as well.

Am I updating my NSArray that's bound to an NSTableView right?

I have a pretty standard setup where I have an array in my controller that I'm binding to an NSTableView. When I add an object to this array with the UI, I add elements by copying the array to a mutable array, adding the contents, then copying it back. I saw this idiom somewhere, but I'm not really sure where, or whether this is actually a sane thing to do. I t works fine on Snow Leopard, but it crashes really badly on Leopard. GDB tells me it happens right at the marked line, where I copy the new array back.
#interface MyAppDelegate : NSObject {
NSArray * urls;
//other declarations
}
#property (nonatomic, retain) NSArray *urls;
#implementation MyAppDelegate
#synthesize urls;
- (void)addThing:(id)sender {
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
}
I'm pretty sure I must be doing something boneheaded here. Is there a better way to do this? Why am I crashing?
NSMutableArray *newUrls = [NSMutableArray arrayWithArray: [self urls]];
[newUrls addObject: newurlimadding];
[self setUrls: [NSArray arrayWithArray: newUrl]];
What did you create newUrls for if not to set it as the new value of urls?
Besides that, there are a couple of things you're doing wrong:
No model objects. Bindings hates this. Unless your table view exists solely to display the different parts of the URL (scheme, host, path, etc.), each in one column, you're not giving Bindings what it wants.
Pay attention to the fields in the Bindings Inspector. Note that there are two: Controller Key and Model Key Path. They're exactly what they say on their tins: The Controller Key is the key for a property of the controller, which should provide model objects. The Model Key Path is the key path for one or more properties in the model, which usually should provide value objects, such as strings and numbers.
You probably need to create a model class and make the URL a property of that class. I'd guess that you have other objects, perhaps in parallel arrays, that you can move to properties of the model class. Do that, so that you're working with Bindings instead of against it.
Not using array accessors. You're just setting the whole array at once, which is inefficient and may cause display problems (such as selection highlights disappearing). Implement and use array accessors for this array property. Accessorizer can help you here.
I actually have no idea whether this will fix your crash because you haven't told us anything about it. You should edit your question to include any exception messages or other crash-describing output, which you'll find in the Run Log/Debugger Console.
Also, since the type of urls can be mutable, you should set it to copy, not retain.
NSArray * things; - since this can be modified you better represent using NSMutableArray instead NSArray.
When ever you need to add some new element to the list just use 'addObject' and insert element to the list.
Your binding will make sure that UI is updated using KVO and KVC.
It looks like the problem was that I had NSURLs as my object type. Once I changed the object in my array to a custom-made KVC compliant object, I wasn't getting any more crashes.
Maybe NSURL isn't KVC-compliant on 10.5, but it is on 10.6?