NSDictionary losing its contents (exc bad access) - iphone

So i have a uiviewcontroller. It has a property of an NSMutableArray with a nonatomic, retain property synthesized.
In the viewDidLoad I init it with the following, and also add a button to the nav bar.
test = [NSDictionary dictionaryWithObjectsAndKeys:
[[SearchField alloc] initWithName:#"Subject" :Text], kSubjectKey,
[[SearchField alloc] initWithName:#"Source publication" :Text], kSourceKey,
[[SearchField alloc] initWithName:#"Keyword" :Text], kKeywordKey,
[[SearchField alloc] initWithName:#"Author" :Text], kAuthorKey,
[[SearchField alloc] initWithName:#"Color" :Bool], kColorKey,
nil ];
NSLog([NSString stringWithFormat:#"lol %d", [test count]]);
The first log, when running through the debugger runs fine. However, I have tied the following code to the button:
-(void)search:(id)sender{
NSLog([NSString stringWithFormat:#"lol %d", [test count]]);
When this code executes the log line crashes with exc bad access. Nothing is happening to the NSDictionary between the viewDidLoad and the button press, so why is this happening? And more importantly, how do I fix it? :)
Cheers
Edit
I thought maybe it was my SearchField class was doing things, so i replaced them with simple strings, the problem still occurs.

You're directly accessing the member variable - you need to go through the accessor - ie:
self.test = [NSDictionary dictionaryWithObje ...
This will make sure your object retains the dictionary.

Make sure test is a class variable, by setting it up as a property of your view controller's header:
#property (nonatomic, retain) NSDictionary *test;
Release it in your view controller's -dealloc method:
[test release], test = nil;
Set its value as follows:
self.test = [NSDictionary dictionaryWithObjectsAndKeys:...];
Access its count property as follows:
NSLog(#"Count: %u", test.count);
Or:
NSLog(#"Count: %u", [test count]);
Note that -count returns an unsigned integer, so you should use %u to format its value.

The dictionary you're creating is autoreleased. You're using a convenience method (dictionaryWithObjectsAndKeys:) which returns a new autoreleased object.
The autoreleased object is being released (and deallocated) some time after its creation, which is why it's crashing later in the program.
You need to retain the returned dictionary, or create a new dictionary using the alloc/init method.
Either way, you need to remember to release the dictionary when you're done with it.
You should read up on the basic memory management rules and conventions for Objective-C.

Related

Iphone Right way to set a property

i have the following code in .h:
#property (nonatomic, retain) NSArray *arrayData;
And in the .m in method initWithNibName:
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
is it right in order to call
[self.arrayData release]
safely in order to release the object?
No, it is not correct to call release on your property. The problem with it is, that you release your property, it will get deallocated, but you didn't set your pointer to nil, so somebody might send a message to your property and get a crash.
What you can do is the following:
self.arrayData = nil; ( which will release the previous saved instance, and set the property to nil)
[arrayData release]; arrayData = nil; (here you are accessing your ivar instead of your property; setting your ivar to nil is a precaution)
[self->arrayData release]; self->arrayData = nil (this is exactly the same as #2)
Hope this helps.
You need to call:
[arrayData release]
Calling [self.arrayData release]; will not have the effect you want it to in either case.
If you're wondering why this is, check this question out: difference between [self.property release] and [property release]
A)
it is a bad idea to do this in your initializer (e.g., initWithNibName:bundle:)
self.arrayData = [NSArray arrayWithObjects:#"Usuario:",#"Password:",nil];
use this instead:
arrayData = [[NSArray alloc] initWithObjects:#"Usuario:",#"Password:",nil];
you should not call these accessors (properties) in initializers or dealloc.
B)
is it right in order to call
[self.arrayData release]
no. in many cases (assuming you implement some of the properties you've declared), you may not be returned the the ivar. you may receive a copy, a placeholder object, or a subclass may have chosen to re-implement the accessors (as some examples). in these cases, it's easy to over-release or over-retain (resulting in evil stuff, like leaks and crashes).
this is typical:
self.arrayData = nil;
unless you are in dealloc of the object which declared the ivar:
- (void)dealloc {
[arrayData release], arrayData = nil;
[super dealloc];
}

iOS - resetting NSMutable Array causes crash

I have an NSMutableArray I'm trying to reload after an async call. The first time it loads like this:
self.sessionProcList = [NSMutableArray arrayWithArray:[result records]];
After the user does some interaction, the same line will be reached to reload the NSMutableArray. This causes the crash
Header file has:
#interface...
NSMutableArray *sessionProcList;
... }
#property (nonatomic, retain) NSMutableArray *sessionProcList;
Say you do:
NSMutableArray *a = [NSMutableArray arrayWithObject: [[NSObject alloc] init]];
NSObject *o = [a objectAtIndex: 0];
[a removeAllObjects];
[o description]; // *BOOM*
The above will [generally -- sometimes not but only by coincidence] crash because o has been deallocated by the time the description method is invoked.
If you have a reference to an object in an array, but have not retained said reference, then said object may be deallocated out from under you when you empty the array.
(And nonatomic vs. atomic is irrelevant.)
If I had to guess, I would say that elements in that array are being referenced from somewhere else. Resetting the array causes the items using the references to crash.
I would check your application for other variables, properties, or UI elements using those variables that have not been release before resetting it.
Because arrayWithArray is a convenience method it gets initialised with an autorelease flag.
You haven't mentioned what the crash / error message is but I am guessing your NSMutableArray is getting released before your second iteraction with it starts.
Try and retain the array for however long you need it with:
self.sessionProcList = [NSMutableArray arrayWithArray:[result records]];
[sessionProcList retain];
And then release it when you're done with it:
[sessionProcList release];
I hope it helps.
Rog

iphone EXC_BAD_ACCESS using my own class

I made my own class derived by NSObject, and here is my code:
-(void) parseRow:(NSDictionary*) dictionary {
NSArray* arName = [[dictionary valueForKey:displayname] componentsSeparatedByString:#"+"];
[self setDriverName:[arName objectAtIndex:0]];
[self setDriverSurname:[arName objectAtIndex:1]];
[arName release]; // this give problem!
}
and in my view:
driverStats = [[DriverStats alloc] init];
// driverStats is declared in the header:
DriverStats* driverStats;
#property (nonatomic,retain) DriverStats* driverStats;
[driverStats parseRow:dictionary];
If I add [arName release] in my class, when I exit from parseRow method, I have the EXC_BAD_ACCESS error.... but it's wrong?? I used array and after I released... I think that the error will be if I didn't release the pointer.. or not???
thanks in advance
The problem is easy to solve. Your NSArray *array only have 0 retainCount. Because you use a factory method componentsSeparatedByString:, it returns you an autoreleased array already. So, you don't need to release your array anymore.
Another thing you need to care about is the setDriverName: and setDriverSurname:, make sure you retain/copy the object there otherwise, when the array is released, those objects are also released and EXEC_BAD_ACCESS again

How do I set an array with another Array?

#property (nonatomic, retain) NSMutableArray *filteredListContent;
----
#synthesize filteredListContent;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSMutableArray *test = [[NSMutableArray alloc] init];
[test addObject:#"test string"];
[filteredListContent addObjectsFromArray:test];
NSLog(#"%#", test);
NSLog(#"Filtered Array is %#", filteredListContent);
[window makeKeyAndVisible];
}
My Log for test shows 'test string' but 'Filtered list array is (null)'
How do I set the array 'filteredListContent' with the array test...
What am I doing wrong? :-(
Are you creating and initializing filtersListContent anywhere? Your code looks right, that should work.
You should also make sure to release your test variable, you have a memory leak here.
You have to actually create filteredListContent, say with [[NSMutableArray alloc] init]. The error you are getting is that you are calling a method, -addObjectsFromArray:, on an object that is still nil: never created. As such, it just returns nil, and the list is never filtered.
filteredListContent is a pointer to a NSMutableArray, it does not have any memory assigned to it, as a result you cannot call methods on it. The compiler does not generate an error because you are passing a message to nil which is perfectly alright.
Thanx for that.
so I changed the line...
[filteredListContent addObjectsFromArray:test];
to read...
filteredListContent = [NSMutableArray arrayWithArray:test];
This done it. I think I understand it now, though I declared it, I never created it...
Thanx.

EXC_BAD_ACCESS when debugging

I'm getting this error when trying to see the contents of a NSMutableArray:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000021
0x94d5a688 in objc_msgSend ()
ViewController.h:
#interface PeopleViewController : UITableViewController {
NSMutableArray *people;
}
#property (nonatomic, retain) NSMutableArray *people;
ViewController.m:
#implementation PeopleViewController
#synthesize people;
In viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
// initialize our people array with an autoreleased object
people = [NSMutableArray array];
... Populate the people array with Person objects.
}
When I'm at the point where I'm modifying the contents of a cell in the tableview, I'm unable to access the people array in gdb when typing 'po self.people':
Person *person = [[Person alloc] init];
person = [self.people objectAtIndex: indexPath.row]; // <--- 'po self.people' called
cell.textLabel.text = person.personName;
Any ideas why I can't access it?
The line
people = [NSMutableArray array];
returns an autoreleased array that will be released on the next iteration of the current run loop. You should retain that:
people = [[NSMutableArray array] retain];
and of course release it in your dealloc method.
However: Apple engineers have often mentioned in conferences to avoid autoreleased instances like this whenever possible in the iPhone, for performance reasons. Try using alloc/init instead:
people = [[NSMutableArray alloc] initWithCapacity:1];
with the corresponding release in the dealloc method. In this case you don't even need to retain (init returns an instance with a retain count of 1, which is what you need).
And justin's comment is correct: you should do this instead:
Person *person = [people objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
and this should work.
is indexPath.row > [people count]?
Also, why are you doing this:
Person *person = [[Person alloc] init]
You're allocating memory, and then pointing to completely different memory.
You can avoid having to fuss with retaining properties by using the self notation to call the accessor and setter methods created by the #synthesize directive.
When you set the people property directly in viewDidLoad it sets the property but does nothing for memory management. However, if you set it with self.people you actually call the synthesized setter method that because of the retain setting of the #property directive will automatically retain the assigned array.
As an aside, I would recommend always using -[NSMutableArray initWithCapacity:] instead of a bare init. It is the actual initializer for the class. You can call it with just '1' if you don't know how big it will be. In the past, I have seen odd problem arise from just using bare init.