Remove all items from UITabBarController - objective-c

I'd like to remove all items from UITabBarController within MainWindow. I can achieve it this way:
self.tabViewController.viewControllers = [NSArray array];
self.tabViewController.customizableViewControllers = [NSArray array];
But what about old controllers that was there? Is this more correct way?
- (void)cleanCurrentTabbar {
for (id ctrl in self.tabViewController.customizableViewControllers) {
[ctrl release];
}
for (id ctrl in self.tabViewController.viewControllers) {
[ctrl release];
}
self.tabViewController.viewControllers = [NSArray array];
self.tabViewController.customizableViewControllers = [NSArray array];
}

Your second option is more correct concept wise as well with respect to memory management because it releases all allocated resources before making it nil.
But rather than giving it an empty array which is in itself an autoreleased object like [NSArray array], you can assign nil.
- (void)cleanCurrentTabbar {
for (id ctrl in self.tabViewController.customizableViewControllers) {
[ctrl release];
}
for (id ctrl in self.tabViewController.viewControllers) {
[ctrl release];
}
self.tabViewController.viewControllers = nil;
self.tabViewController.customizableViewControllers = nil;
}
Also as James Webster has said in blow comment:
"You may or may not need to release depending on the type of property viewControllers and customizableViewControllers are"
Hope this helps you.

That's cleaner in terms of memory yes. You should release objects when you are finished with them, that includes before reassigning.
However, I notice you are using self.tabViewController.viewControllers. Is that property assign or retain? If it's retain, the release will be done internally.

As #Parth Bhatt told, releasing viewControllers items looks like good idea. Maybe. But in my case it leads to the strange EXC_BAD_ACCESS errors:
So I end up with self.tabViewController.viewControllers = nil; and its works just fine.

Related

Is it ok to retain and autorelease at the same time?

I have the following code:
#interface MyClass : NSObject
{
NSMutableArray *items;
}
#end
#implementation MyClass
-(Item *)getItem
{
if(items.count < 1)
{
[self buildItemsArray];
}
Item *item = [[[items objectAtIndex:0]retain]autorelease];
[items removeObjectAtIndex:0];
return item;
}
-(void)buildItemsArray
{
// ...
[items addItem:someNewItem];
[items addItem:someOtherNewItem];
}
#end
I have a function that returns an item. If the items go down to 0, then the items array is built again inside buildItemsArray. My game logic requires that when I return an item, I need to remove it from the array. So, I am using a retain to ensure that the item is valid till the return line (since the only other known retain happened when the item was added to the items array), and an autorelease to ensure it gets cleaned up later. I have checked that this code neither crashes nor leaks. I wonder if:
a) This is OK - the reason I ask is that I have predominantly seen code with alloc/init/autorelease, and haven't run into this case with retain/autorelease
b) Is there some reason to alloc/init/autorelease instead:
Item *item = [[Item alloc]initWithItem:[items objectAtIndex:0]autorelease];
Thanks
This is generally ok:
Item *item = [[[items objectAtIndex:0] retain] autorelease];
[items removeObjectAtIndex:0];
return item;
Though this would make the intent clearer (returning an autoreleased object):
Item *item = [[items objectAtIndex:0] retain];
[items removeObject:item];
return [item autorelease];
No reason to alloc/init. That just adds unnecessary overhead.
Like I said in my comment, if this is a (relatively) new project, you really really really should be using ARC and not worry about these things anymore.

Using Autoreleased Objects in iOS apps

To return a NSArray or NSDictionary, I have seen most people use the below implementation and this is also what some books suggest. (iOS Development A Practical Approach - )
OPTION 1
-(NSArray*)listOfStudents{
NSMutableArray *temp = [[NSMUtableArray alloc] init];
//Add elements to the array
//
//
//
NSArray *students = [NSArray arrayWithArray:temp];
return students;
}
-(void)viewWillAppear{
self.studentsList = [self listOfStudents];
}
But can this same be done by the below way also?
OPTION 2
-(NSArray*)newListOfStudents{
NSMutableArray *temp = [[NSMUtableArray alloc] init];
NSArray *students = [[NSArray alloc]initWithArray:temp];
[temp release];
//Add elements to the array
//
//
//
return students;
}
-(void)viewWillAppear{
NSArray *array = [self newListOfStudents];
self.studentsList = array;
[array release];
}
Assume these methods are called in the main thread itself.
Interms of memory usage , I think that the second option is good, because it does not create autoreleased objects, because they are released only at when the autorelease pool is drained.
I assume that the main autorelease pool is drained only when the app quits. So if the method in OPTION 1 is used many times ,(since they are getting called in ViewWillAppear) I think that many lists will be in autorelease pool being released only when the app quits.
So is the OPTION 2 approach the better approach?
UPDATE:
I have updated the viewWillAppear implementation for better clarity.
I think in the second example you meant to call
self.studentsList = [self newListOfStudents];
In case that studentsList is a retained property, this would leak now.
Also, that temp array in both examples is just useless overhead. In the second example it's plain nonsense.
The cleanest solution is
-(NSArray *)listOfStudents {
NSMutableArray *list = [NSMutableArray array];
// Add things to array
return list;
}
Two more advices:
1) you might run the static analyzer over your code, which will point to memory issues.
2) if you feel more confident with memory management, switch over to ARC.

Regarding memory management in Objective C

According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!

Why Do I Have to Create An Object and Assign It to A Property in Objective C?

So I had this code, and it did not work:
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
self.resultsArray is nil. But then I changed it to this:
NSMutableArray *myDataArray = [[NSMutableArray alloc] init];
for (NSDictionary *item in data){
[myDataArray addObject:item];
}
self.resultsArray = myDataArray;
[myDataArray release];
and now it worked. self.resultsArray is now populated
So I'm a beginner in Objective C and I was wondering why can I not just directly use it in the property's addObject. Why did I have to create another mutable array, populate it, assign it to the resultsArray property and release the mutable array I made?
Thanks in advance!
EDIT: Also, in a lot of books I've been working on, this is done a lot.
simple answer
You didn't initialize self.resultArray before adding objects to it. It is just a pointer to the value which is nil until you alloc it.
self.resultArray = [[NSMutableArray alloc] init]; before adding objects to it will solve the issue.
However, this way of alloc'ing will create a memory leak, therefore it is not shown in books and examples. Memory leak can happen if the self.resultArray property is marked as retain and by calling alloc it will be retained 2 times.
If self.resultsArray is nil, then [self.resultsArray addObject:item] will NOT add an object to the array, it will just do nothing (because the array will be nil by default, and sending messages to nil is a no-op in Objective-C). When you create a mutable array as a local variable, you can add things to it — then if you assign it to the property, well, everything works as you expect and self.resultsArray will no longer be nil.
Typically when you have properties like this, you'd set them up in your init method:
- (id)init {
// ...
self.resultsArray = [NSMutableArray array];
// or access the ivar directly:
// _resultsArray = [[NSMutableArray alloc] init];
// ...
}
Then as soon as your object is initialized you'll be able to add things to the array. Again, if you don't do this, it will be nil by default, and [self.resultsArray addObject:item] will have no effect.
Chances are you are not initializing the array (I'm going to assume myDataArray is an NSMutableArray).
In your init method, call myDataArray = [NSMutableArray array]; and it'll work
The important thing to note is that you're not creating another mutable array as you didn't have an array to start with. Merely declaring a property or variable does not create an object to go along with it. That's why self.resultsArray starts out as nil.
The working code you have is designed to allow you to explicitly release the array as you are retaining it twice: once when you alloc it and once when you assign it to your property. You only want one of those retains, so you release once.
You could just do:
self.resultsArray = [[NSMutableArray alloc] init];
[self.resultsArray release];
for (NSDictionary *item in data){
[self.resultsArray addObject:item];
}
This is less code, but it's not as clear. Clarity is important.

How am I meant to release an array returned from a class method?

I have a section of my code which calls a method returning an NSMutableArray like so:
+(NSMutableArray *)method {
NSMutableArray *rgb = [[NSMutableArray alloc] initWithObjects:....., nil];
return rgb;
}
It gives me a leak every time it's called. Putting [rgb release]; after return doesn't appear to work. Putting it before return makes my app crash. Also putting in an autorelease makes my app crash. Suggestions?
+(NSMutableArray *)method {
NSMutableArray *rgb = [[NSMutableArray alloc] initWithObjects:....., nil];
return [rgb autorelease];
}
Alternatively:
+(NSMutableArray *)method {
NSMutableArray *rgb = [NSMutableArray arrayWithObjects:....., nil];
return rgb;
}
IF this still crashes, then the problem is most likely outside of that method, not within.
But this gives me a leak every time
it's called. Putting a [rgb release];
after return doesn't appear to work.
putting it before return makes my app
crash. Also putting in an autorelease
makes my app crash. Suggestions?
You need to read the Cocoa Memory Management Guidelines. It makes it quite clear that such a method as that must autorelease the returned object:
+(NSMutableArray *)method {
NSMutableArray *rgb = [[NSMutableArray alloc] initWithObjects:....., nil];
return [rgb autorelease];
}
Or, alternatively:
+(NSMutableArray *)method {
NSMutableArray *rgb = [NSMutableArray arrayWithObjects:....., nil];
return rgb;
}
An over-retain (like in your code) will not cause a crash (at least not until you run out of memory). If you have a crash, it is most likely because your are mismanaging memory in other ways.
Try using Build and Analyze and fixing all of the issues it identifies. If it still crashes, post the backtrace of the crash if you can't figure out why.