Objective-c simple memory question - objective-c

If I have a variable in my view controler
viewcontroller.m
#interface MemoryTestViewController : UIViewController
{
NSMutableArray *array;
}
#end
in my implementation
- (void)viewDidLoad
{
[super viewDidLoad];
NSMutableArray *aux = [[NSMutableArray alloc] initWithCapacity:1];
array = aux;
[aux release];
// Do i have to do array release?
}
Do i have to release my variable array somewhere? Theoricaly i havent allocated that variable...
I testes the memory leaks and even if i dont release anything the instruments doesn't detect any leak.

No, you don't need to release. All you do is assign the pointer of aux to your array variable.
array is invalid at the moment where you release aux.
This is probably not as intended. If you want to work with array, you'll have to retain it.

You've already released the array with [aux release]; -- you in fact have the opposite problem to a leak: an over-release.
Assignments in Objective-C are just assignments of pointers; there's no copying or automatic memory management. When you say array = aux;, array now points to the exact same object as aux. If you then get rid of aux by releasing it (and therefore letting it be deallocated), array doesn't point to anything anymore.*
You have a couple of options for fixing this:
(Simplest) Assign the newly-created array directly to array:
array = [[NSMutableArray alloc] initWithCapacity:1];
This gives you ownership of the new array, under the name array. Don't release it until you are done with it (possibly in dealloc; certainly not in this method).
(Best) Create a declared property for array and let that mechanism handle the memory management for you:
#interface MemoryTestViewController : UIViewController
{
NSMutableArray *array;
}
#property (copy, nonatomic, setter=setArrayByMutableCopy) NSMutableArray * array;
#end
#implementation MemoryTestViewController
#synthesize array;
// Properties can't automatically make mutable copies, so you need to create
// your own setter method.
- (void) setArrayByMutableCopy: (NSMutableArray *)newArray {
NSMutableArray * tmp = [newArray mutableCopy];
[array release];
array = tmp;
}
...
*Or, rather, it points to a place where there used to be a valid object, which is a great way to make your program crash.

No. Assigning an object to a variable does not retain it. However if you plan to use that variable for a while, you should retain it and release it when you are done with it.
alloc raised the retain counter to 1 and [aux release] set it to 0

You should add a property:
#property (nonatomic, retain) NSMutableArray *array;
and later in your viewDidLoad:
// wrong, leaks: self.array = [[NSMutableArray alloc] initWithCapacity:1];
array = [[NSMutableArray alloc] initWithCapacity:1];
and yes, somewhat later release it, probably in dealloc..

Related

ios initialize an instance inside a function to use outside

the xcode analyzer tell me that a method returns an Objective-C object with a +1 retain count:
but the self.athletes is an object that I need also outside my function... how can I solve this 'warning?
thanks again
the athletes is declared like this:
NSMutableArray *athletes;
#property (nonatomic, retain) IBOutlet NSMutableArray *athletes;
Replace that line with this one:
self.athletes = [NSMutableArray array];
I wrote full explanation here : Memory Management for properties with retain attribute
Since your property is defined with "retain", using the dot notation will result in an extra retain. The return from the [[NSMutableArray alloc] init] has a retain count of 1, and then when you set the property using the setter function generated by the property declaration it will have a retain count of 2.
To fix, either:
self.athletes = [NSMutableArray array]; // Returns an autoreleased object
Or, you could also do this:
athletes = [[NSMutableArray alloc] init]; // Doesn't use the setter generated by the property declaration, so doesn't retain again.
There is a nice way to handle this (and you have already used this pattern while creating UI ).
NSMutableArray *athletesTemp = [[NSMutableArray alloc] init];
self.athletes = athletesTemp;
[athletesTemp release];
Here you don't need to carry the load of an auto release object.

Question about release the object

I have a class which contain a NSArray object.
like this
#import <Foundation/Foundation.h>
#interface myClass : NSObject {
NSArray *myArray;
}
#property (nonatomic, retain) NSArray *myArray;
#end
In .m file, I init myArray in init method,and release myArray in dealloc method.
in a method, I create its object and add it to a NSMutableArray.
myClass *my = [[myClass alloc] init];
NSLog(#"init finish %d",[my retainCount]);
NSMutableArray *a = [[NSMutableArray alloc] init];
[a addObject:my];
NSLog(#"array added finish %d",[my retainCount]);
NSLog(#"array added finish %d",[my.myArray retainCount]);
[my release];
When i add object "my" to NSMutableArray, the retainCount of "my" was added.
but myArray wasn't. Did it mean that I must retain myArray by my self?
or something other I can do.
Can this code work normal after I release "my" object?
[a objectAtIndex:0];
Thanks!
Another great example of exactly why you should...
NEVER call -retainCount!
retainCount is useless, misleading and a waste of your time.
In this case, the reason why the retain count happens to be zero "unexpectedly" is because my.myArray returns nil. That happens because you never assign the created mutable array to myArray.
You need something like this (Class name capitalized to follow convention):
MyClass *my = [[MyClass alloc] init];
NSMutableArray *a = [[NSMutableArray alloc] init];
my.myArray = a;
[a addObject:my];
Note that this creates a retain cycle between my and the array. I.e. you will need to remove my from the array manually (or remove the array from my manually) whenever you release myArray and, of course, you can't do that in dealloc because dealloc will never be called until my has been removed from myArray.
I am not sure if that is the full source code above but for my.myArray to be retained you have to actually assign something to it.
So in your above example you created an NSMutableArray:
NSMutableArray *a = [[NSMutableArray alloc] init];
And then you added your class to it:
[a addObject:my];
But no where have you created an array and assigned it to myClass as in:
NSArray *anArray = [[NSArray alloc] init.....];
my.myArray = anArray;
At that point, myArray will get a reference to an object and will retain it (since you specified retain in your myArray prop declaration).
Perhaps if you clarified what it is you are trying to do or posted some more full source code?

I wonder about releasing variables

UIView *view; //1
UISegmentedControl *scopeBar; //2
NSMutableArray *array; //3
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) UISegmentedControl *scopeBar;
#property (nonatomic, retain) NSMutableArray *array;
.m
#synthesize view, scopeBar, array;
for (id subView in [view subviews]) {
if ([subView isMemberOfClass:[UISegmentedControl class]]) {
scopeBar = (UISegmentedControl *)subView;
}
}
array = [[NSMutableArray alloc] init];
- (void)dealloc {
}
I think that only the third of the variables has to be released in the dealloc method.
Is that right?
Yes, (array needs to be released) because you alloc it. So, it's programmer's responsibility to release it. So -
- (void)dealloc {
[ array release ] ;
// Any other resources alloc, init, new should be released
}
For more info on what to release, Memory management - ObjectiveC
And I think you will find good suggestions in this question about your query
Why should we release?
Contrary to some of the answers, you have to release your outlet (view) as well, and not only in the dealloc but also in the viewDidUnload, the easiest way is to set it to nil :
self.view = nil;
Also note that if you don't access your properties but your instance variables (i.e. without self. prefix) your retain attribute won't help you and you are not retaining the object. That means that as soon as scopeBar would be removed out of the subViews of the view, it will be released and you end up accessing a zombie.
As a rule of thumb, it's best to use the properties accessor everywhere except in the init methods so that you don't have to deal with the memory management explicitly. Setting them to nil in the dealloc and viewDidUnload in case of outlets should be enough then.
Also, don't do what Jenifer suggested and once you've called a release on a variable, don't set the property to nil, that would overrelease it.
I think that only the third of the variables has to be released in the dealloc method. Is that right?
// no. your dealloc should look like this:
- (void)dealloc {
// note: *not* using accessors in dealloc
[view release], view = nil;
[scopeBar release], scopeBar = nil;
[array release], array = nil;
[super dealloc];
}
// your assignment of `scopeBar` should look like this:
...
self.scopeBar = (UISegmentedControl *)subView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `view` should look like this:
...
self.view = theView;
...
// you want to retain the view, as advertised.
// consider avoiding an ivar if you can easily access it.
// your assignment of `array` should look like this in your initializer:
// note: *not* using accessors in initializer
...
// identical to `array = [[NSMutableArray alloc] init];`
array = [NSMutableArray new];
...
// and the assignment of `array` should look like this in other areas:
...
self.array = [NSMutableArray array];
...
// you're likely to be best suited to declare your array as
// follows (assuming you really need a mutable array):
...
NSMutableArray *array; // << the declaration of the ivar
...
...
// the declaration of the public accessors.
// note the array is copied, and passed/returned as NSArray
#property (nonatomic, copy) NSArray *array;
...
// finally, the implementation manual of the properties:
- (NSArray *)array {
// copy+autorelease is optional, but a good safety measure
return [[array copy] autorelease];
}
- (void)setArray:(NSArray *)arg {
NSMutableArray * cp = [arg mutableCopy];
// lock? notify?
NSMutableArray * prev = array;
array = cp;
[prev release], prev = nil;
// unlock? notify? update?
}
other answers assume that dangling pointers (e.g., you still hold a pointer to view, although the view may have changed behind your back) are allowable.
they should not be allowed in real programs. they are extremely dangerous, and it can very difficult to reproduce errors they cause. therefore, you must ensure you own a reference to the pointers you maintain/hold.
you should also use the accessors in the public interface for the subclasser's sake - in case they override them. if you don't want to allow/support that, consider simply using a private variable.
As i think you should release and set them nil because you have made them properties so do this:-
in your dealloc
[array release];
self.array=nil;
self.scopeBar=nil;
self.view=nil;

Memory management technique for Objective-C iVars/properties

Is the following code doing anything unnecessary?
#interface MyClass {
NSArray *myArray;
}
-(void)replaceArray:(NSArray *)newArray;
#implementation MyClass
-(void)replaceArray:(NSArray *)newArray {
if( myArray )
{
[myArray release];
myArray = nil;
}
myArray = [[NSArray alloc] initWithArray: newArray];
}
#end
What if I made the following changes:
1) Made myArray a property:
#property (nonatomic, retain) NSArray myArray;
2) Changed the assignment to:
self.myArray = [NSArray arrayWithArray: newArray];
Would that allow me to remove the conditional?
You don't need the conditional at all; you can message nil (including a release), and nothing will happen. You also don't need to allocate a new array; you can retain the one passed to you instead. If you're worried about actually getting an NSMutableArray, you can make a copy. I'd do this:
- (void)replaceArray:(NSArray *)newArray
{
[myArray autorelease];
myArray = [newArray copy];
}
Or, if you don't want to use autorelease, you could do:
- (void)replaceArray:(NSArray *)newArray
{
if (myArray != newArray) {
[myArray release];
myArray = [newArray copy];
}
}
You can already get rid of the conditional. If the array is nil, then you'll be sending a message to nil, which is a no-op. The assignment to nil is pointless either way as well. And if you make it a retain property, explicitly releasing the old value is wrong.
However, there is one case where that code will not work correctly: When the argument is the current value. In that case, you'll release the current value and then try to use the released object (which may already have been dealloced) to create a new array.
Imaging the following:
MyClass * myObj;
// init myObj
NSArray * array = [myObj myArray];
[myObj replaceArray:array];
In this case, myArray and newArray are the same, which means you're using it after it being released. To solve this problem, all you need to do is remove the replaceArray: method, and implement the property as #synthesize myArray. So the above code changes to
MyClass * myObj;
// init myObj
NSArray * array = [myObj myArray];
[myObj setMyArray:array];
and your problem is solved by the synthesized implementation.
Note that you are setting your value by creating a new array:
myArray = [[NSArray alloc] initWithArray: newArray];
if this is the behaviour you want, you should change your property definition to copy instead of retain:
#property (nonatomic, copy) NSArray myArray;
I've voted up mipadi because his answer is right in the context of the question you asked, but why not just use a property and do away with replaceArray: altogether:
#interface MyClass {
NSArray *myArray;
}
#property (copy) NSArray* myArray;
#end
#implementation MyClass
#synthesize myArray;
-(void) dealloc
{
[myArray release];
[super dealloc];
}
#end

'initializing' a property which is retained

In the iPhone objective-c world, I've seen this pattern everywhere and I use it myself all the time without really understanding what is going on:
In Test.h
#interface Test: UIViewController
{
NSMutableArray *testArray;
}
#property (retain, nonatomic) NSMutableArray *testArray;
And in Test.m
#implementation Test
#synthesize testArray
- (void) viewDidLoad
{
// why do we do this?
NSMutableArray *init = [[NSMutableArray alloc] init];
self.testArray = init;
[init release];
[self.testArray addObject: #"A"]; // why can't I do this directly?
...
}
- (void) dealloc
{
[testArray release];
[super dealloc];
}
My question is: if testArray has a retain on it when it's declared in the property, why do we need to create a new NSMutableArray init object, assign that to testArray and release? Why can't I just start using testArray in viewDidLoad without doing anything else?
I know there's some debate over the best way of doing this (creating a new object, or using an autorelease object), but in both cases, we end up with testArray with a retain count of 1. Which I believe the 'retain' property already gives it. So why the need to create this init object?
The 'retain' property doesn't automatically create an NSMutableArray for you. Rather, it simply indicates that whenever you do assign something to that property, it will be retained.
If your code were this:
- (void) viewDidLoad
{
[self.testArray addObject: #"A"];
}
Then self.testArray would be nil, and thus it would be essentially a no-op. Until you assign something to self.testArray, it's empty.
Here's what's going on.
- (void) viewDidLoad
{
// we need to assign an NSMutableArray to self.testArray.
NSMutableArray *init = [[NSMutableArray alloc] init];
// The array has been retained once (by the call to |alloc|)
self.testArray = init;
// The array is assigned to a property with the 'retain' attribute
// Thus, the array has now been retained twice
[init release];
// We release the array, so it now is retained once.
// We now have an array in self.testArray, so we can add something to it.
[self.testArray addObject: #"A"];
}
The "retain" in the #property directive specifies that the setter should retain the input value instead of simply copying the value. It has nothing to do with allocating (setting aside memory) and initializing (constructing the object) the object. retain on the #property directive simply increments the retain count when the setter is called (which alllows you to do something like self.myobject = something without specifically calling retain.