Adding item to NSMutableArray with function results in no - iphone

I have the following utility method declared, where "players" is a NSMutableArray object and a property of my application's view controller. My problem is that "players" still has 0 objects after this method is called.
-(void)addPlayerNamed:(NSString *)name withLife:(NSNumber *)life{
NSMutableDictionary *newPlayer = [NSMutableDictionary dictionaryWithCapacity:1];
[newPlayer setObject:life forKey:name];
[players addObject:newPlayer];
}

Have you set players to anything? You have to call this before using it, maybe in the initialiser.
players=[[NSMutableArray alloc] init];
If players is a property, you probably also want to use
self.players
instead of
players
(Although the latter will still work)

In all likelihood, players is nil. Any message to nil returns 0 or nil.

Related

Question about autorelease object in Objective-C

I have an instance variable called users defined as NSMutableArray.
I use that variable for fill an UITableView.
In viewDidLoad I initialize it with:
users = [[MySingleton sharedClass] getUsers];
This is the getUsers method:
- (NSMutableArray *)getUsers
{
...
NSMutableArray *listArray = [[NSMutableArray alloc] init];
for (NSDictionary *dict in jsonObject) {
...
[listArray addObject:element];
...
}
return listArray;
}
In this way all it works fine. The problem is when I set listArray as autoreleased object.
NSMutableArray *listArray = [[[NSMutableArray alloc] init] autorelease];
or
return [listArray autorelease];
Sometimes the app crash with EXC_BAD_ACCESS.
Why this? Isn't correct set autorelease listArray?
Assuming that users in users = [[MySingleton sharedClass] getUsers] is an instance variable, you're forgetting to take ownership of the array. When you want to claim ownership of an object (such as this array), you need to send it retain to tell it you want it to stick around. And when you're finished with it, you need to send it release to tell it so. Setters handle this for you, so it's generally a good idea to use setters outside of init and dealloc methods. So assuming you have a setter for users, you could do one of these:
self.users = [[MySingleton sharedClass] getUsers];
/* OR */
users = [[[MySingleton sharedClass] getUsers] retain];
The first way is usually better, but you don't want to call setters in init… or dealloc methods because they might have side effects that are undesirable there. Since you're not in one of those methods here, you can just use the first.
You have created and assigned an autoreleased object to user. By specifying autorelease you are saying that system could free it. So when it reaches the end of autorelease pool its removed from memory. That is why when you try to access it late it crashes. So if you need it to be global then you need to retain it.

Accessing NSDictionary problem

I'm only new to iPhone development, so my apologies if this is a silly question. I'm building various apps based on a book I'm reading and one of their suggestion was to build a mini web browser. I thought this would be easy, but while most of it is, I'm seriously struggling with the NSDictionary.
I have a UISegmentedControl used to display various bookmarks. The bookmark name that is displayed on the buttons of the UISegmentedControl is going to be my key and the url is the value associated with it.
I first try to declare an NSDictonary as a private (global variable), but since I could not get it to work, I resorted to declare it in my header file as follows:
#property (nonatomic, retain) NSDictionary *bookmarks;
I synthesize it and I initialized it in the viewDidLoad as follows:
- (void)viewDidLoad
{
bookmarks = [NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft",
#"http://www.google.com",
#"Google",
#"http://www.apple.com",
#"Apple",
#"http://msdn.microsoft.com",
#"MSDN", nil];
[super viewDidLoad];
}
I then associated a control with my segmented control and when the event is triggered and the function is called I've got the following code which is called:
- (IBAction) getShortcut:(id)sender
{
NSString *shortcut;
shortcut = [shortcuts titleForSegmentAtIndex:shortcuts.selectedSegmentIndex];
NSString *url = [bookmarks valueForKey:shortcut];
//[self navigateTo: url];
[url release];
}
When a button from the UISegmentedControl is clicked, I extract the value and stored it into shortcut and then I try to use the shortcut variable as a key to extract the associated value from the NSDictionary "bookmarks" but it keeps crashing on NSString *url = [bookmarks valueForKey:shortcut];
and bombs out of my function and displays the usual error EXC_BAD_ACCESS
Any help would be greatly appreciated.
T.
You have two options. one is to deal with the ivar directly as #Matt S. posted. Note that in this case you need to keep you object with enough retain count. You're using and auto released object and causing the error.
The other option is to use the property you already defined:
self.bookmarks = [[NSDictionary ...]];
And the property retains it.
That dictionary is autoreleased.
Try this:
self.bookmarks = [NSDictionary dictionaryWithObjectsAndKeys...]
You didn't retain the NSDictionary.
Do:
bookmarks = [[NSDictionary
dictionaryWithObjectsAndKeys:#"http://www.microsoft.com",
#"Microsoft", nil] retain];
The problem is, that you do not retain "bookmarks" in the viewDidLoad method. There is an naming convention mentioned somewhere in the Apple docs: If an intialisation method starts with "init..." the returned object is retained, if not you have to do it yourself. The "dictionaryWithObjectsAndKeys" returns and object with retain count 0, which means, that after the scope of assignment (your viewDidLoad method) it is immediatly released again.
So just put a
[bookmarks retain];
after your initalisation and you are done. Another solution which does the retaining for you
bookmarks = [[NSDictionary alloc] initWithObjectsAndKeys ...];
And you shouldn't release the url in your action. It gets released, once you release the dictionary

Objective-C Memory Management Question, NSMutableArray

I have a UIViewController. At the top of the UIViewController, I have declared
NSMutableArray *contacts;
In my viewDidLoad method, I call [self getContacts] which basically initializes my contacts array. It begins by initializing the array, and then it adds some objects:
if(contacts == nil)
contacts = [[NSMutableArray alloc] init];
[contacts removeAllObjects];
[contacts addObjectsFromArray:[some objects]];
So, now my contacts is initialized. In my viewDidLoad method, I even use contacts, and it works great. Later on, in a method, I need to retrieve the elements of contacts, however I am getting an EXC_BAD_ACCESS. Why is this? Why doesn't my contacts array keep the objects that I initialized it with in the beginning, and how do I fix this?
EDIT:
The error comes when I select a NavigationBarItem which then triggers a method buttonWasPressed. In that method, I simply have the following:
-(void)buttonWasPressed:(id)sender {
NSLog(#"button was pressed");
if(contacts == nil)
NSLog(#"contacts is nil!");
NSLog(#"contacts = %#",contacts);
}
And I see "button was pressed" printed, but then there is an EXEC_BAD_ACCESS.
That code all looks good, nothing wrong there. I would guess you are over-releasing elsewhere. Turn on Zombies - add NSZombieEnabled to YES in the executable arguments and it will break on the line so you can see what object is being over-released.

How to release an object in a forin loop?

I'm new to cocoa / objective-c and i'm struggeling with the releases of my objects. I have the following code:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
}
The analyzer shows me that the "gastrocategory" defined in the for is a potential memory leak. But i'm not sure if i can release this at the end of the for loop?
Also at the following code:
- (NSArray *)eventsForStage:(int)stageId {
NSMutableArray *result = [[NSMutableArray alloc] init];
for (Event *e in eventList) {
if ([e stageId] == stageId) {
[result addObject:e];
}
}
return result;
}
The Analyzer tells me that my "result" is a potential leak. But where should I release this?
Is there also a simple rule to memorize when i should use assign, copy, retain etc. at the #property ?
Another problem:
- (IBAction)showHungryView:(id)sender {
GastroCategoriesView *gastroCategoriesView = [[GastroCategoriesView alloc] initWithNibName:#"GastroCategoriesView" bundle:nil];
[gastroCategoriesView setDataManager:dataManager];
UIView *currentView = [self view];
UIView *window = [currentView superview];
UIView *gastroView = [gastroCategoriesView view];
[window addSubview:gastroView];
CGRect pageFrame = currentView.frame;
CGFloat pageWidth = pageFrame.size.width;
gastroView.frame = CGRectOffset(pageFrame,pageWidth,0);
[UIView beginAnimations:nil context:NULL];
currentView.frame = CGRectOffset(pageFrame,-pageWidth,0);
gastroView.frame = pageFrame;
[UIView commitAnimations];
//[gastroCategoriesView release];
}
I don't get it, the "gastroCategoriesView" is a potential leak. I tried to release it at the end or with autorelease but neither works fine. Everytime I call the method my app is terminating. Thank you very much again!
In your loop, release each gc after adding it to the list since you won't need it in your loop scope anymore:
gastroCategoryList = [[NSMutableArray alloc] init];
for (NSDictionary *gastrocategory in gastrocategories) {
NSString *oid = [gastrocategory objectForKey:#"id"];
GastroCategory *gc = [[GastroCategory alloc] initWithId:[oid intValue] name:[gastrocategory objectForKey:#"name"]];
[gastroCategoryList addObject:gc];
[gc release];
}
In your method, declare result to be autoreleased to absolve ownership of it from your method:
NSMutableArray *result = [[[NSMutableArray alloc] init] autorelease];
// An alternative to the above, produces an empty autoreleased array
NSMutableArray *result = [NSMutableArray array];
EDIT: in your third issue, you can't release your view controller because its view is being used by the window. Setting it to autorelease also causes the same fate, only delayed.
You'll have to retain your GastroCategoriesView controller somewhere, e.g. in an instance variable of your app delegate.
BoltClock's answer is spot-on as to the first part of your question. I'll try to tackle the rest.
Assign is for simple, non-object types such as int, double, or struct. It generates a setter that does a plain old assignment, as in "foo = newFoo". Copy & retain will, as their names imply, either make a copy of the new value ("foo = [newFoo copy]") or retain it ("foo = [newFoo retain]"). In both cases, the setter will release the old value as appropriate.
So the question is, when to copy and when to retain. The answer is... it depends. How does your class use the new value? Will your class break if some other code modifies the incoming object? Say, for example, you have an NSString* property imaginatively named "theString." Other code can assign an NSMutableString instance to theString - that's legal, because it's an NSString subclass. But that other code might also keep its own reference to the mutable string object, and change its value - is your code prepared to deal with that possibility? If not, it should make its own copy, which the other code can't change.
On the other hand, if your own code makes no assumptions about whether theString might have been changed, and works just as well whether or not it was, then you'd save memory by retaining the incoming object instead of unnecessarily making a copy of it.
Basically, the rule, which is unfortunately not so simple sometimes, is to think carefully about whether your own code needs its own private copy, or can correctly deal with a shared object whose value might be changed by other code.
The reason you can release gc after it is added to the gastroCategoryList is that when an object is added to an array, the array retains that object. So, even though you release your gc, it will still be around; retained by the gastroCategoryList.
When you are returning a newly created object from a method, you need to call autorelease. This will cause the object to be released only after the runtime leaves the scope of the calling method, thereby giving the calling method a chance to do something with the returned value.
Note that if your method starts with the word copy or new, then you should not autorelease your object; you should leave it for the calling method to release.
As for copy vs retain vs assign... as a general rule, copy objects that have a mutable version, such as NSArray, NSSet, NSDictionary, and NSString. This will ensure that the object you have a pointer to is not mutable when you don't want it to be.
Otherwise, use retain whenever you want your class to be ensured that an object is still in memory. This will apply to almost every object except for objects that are considered parents of your object, in which case you would use assign. (See the section on retain cycles here).
Also note that you have to use assign for non-object types such as int.
Read through the Memory Management Programming Guide a bit; it's quite helpful.

Passing a nil object to a function

I was wondering if objective C does any check to see if a pointer to an object is nil before calling the function.
For example, say I have a
myObject* ptr;
and initialize
ptr = nil;
and call
[self myFunction:ptr];
where myFunction is my own function and does no check to see if the object is nil. I heard somewhere that objective C will not call the function if it is nil? Is this true and would my code be safe?
The reason I ask is because I'm implementing a universal app, and have an UIView instance that will only work for the ipad. But, I do many function calls for this view, and instead of doing condition checks to see if it is an ipad before calling the function, it would be great if I could set the view as nil if it's an iphone.
Also, if the interface builder allocated the object and I set the pointer to nil, will there be a memory leak or will the builder know to dealloc the object?
Thanks
You can always provide a method with a nil argument, but I think what you might be misunderstanding is about messaging nil.
MyClass *object = nil;
[object doSomething]; // nothing done, because object is nil
object = [[MyClass alloc] init];
[object doSomething]; // does something, because object points to an instance
To demonstrate providing nil as an argument:
NSMutableDictionary *myDict = [NSMutableDictionary dictionary];
[myDict setObject:#"Value 1" forKey:#"Key 1"];
[myDict setObject:nil forKey:#"Key 1"]; // perfectly valid
// myDict is empty again after setting nil value for "Key 1".
myDict = nil;
[myDict setObject:#"Value 1" forKey:#"Key 1"]; // nothing happens!
In the cases above, object and myDict are called the “receiver”. When the receiver is nil, no action is performed. This is quite different than other programming languages, for example, in C++ the following is not valid:
MyClass *object = NULL;
object->doSomething(); // oops, this is not allowed
As for the memory, if you have the object in the NIB file and then set its outlet to nil in the code, there will be a memory leak. You should release the object and then set it to nil.
It might be a good idea in that case, though, to simply create the object if it's an iPad and leave the variable as nil if it's an iPhone. That way you don't have to deal with any stray references that may crop up if you create the object in the NIB file. That may or may not be an issue, but it's probably better to create conditionally rather than destroy conditionally.