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?
Related
I have a class with a NSArray property using ARC with nothing fancy...
#import <Foundation/Foundation.h>
#interface MyClass : NSObject
property(nonatomic, strong) (NSArray *) myArray;
#end
#import "MyClass.h"
#implementation MyClass
#synthesize myArray = _myArray;
#end
If an attempt is made to use a method inside of another class to try to set that array it does not set it if only the array is passed; however, it does set if the class is passed, I'm not sure why that is....
The code that attempts this both ways is below...
MyClass *myClass = [[MyClass alloc] init];
[self setArrayByPassingArray:myClass.myArray];
NSLog (#"%#", myClass.myArray)
//result is null
[self setArrayByPassingClass:myClass];
NSLog (#"%#", myClass.myArray)
//result is test, test2...
-(void)setArrayByPassingArray:(NSArray *)arrayToSet {
arrrayToSet = [[NSArray alloc] initWithObjects: #"test", #"test2", nil];
}
-(void)setArrayByPassingClass:(MyClass *)classWithArrayToSet {
classWithArrayToSet.myArray = = [[NSArray alloc] initWithObjects: #"test", #"test2", nil];
}
I tried some other methods with just strings and the strings are not changed, so I'm not sure why they are changed if class containing them is passed...
Your setArrayByPassingArray: method in the first example assigns the newly created NSArray to its parameter, which is passed by value, and is promptly discarded upon exiting from the method. What happens here is that a copy of the reference to myArray is made (not a copy of the array, only a copy of a reference to that array) before calling setArrayByPassingArray:. That copy is no longer attached to the myArray member of MyClass.
Your second example is not passing a class - it's passing an instance of the class, and then it correctly uses the dot notation to assign the myArray property in your instance. That's why this second example works, and the first example does not.
If you use NSMutableArray instead of NSArray throughout your program, you can rewrite your first example to make it work:
-(void)setArrayByPassingArray:(NSMutableArray *)arrayToSet {
[arrrayToSet setArray:[[NSArray alloc] initWithObjects: #"test", #"test2", nil]];
}
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..
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.
#interface{
NSArray *array;
}
#property (nonatomic, retain) NSArray *array;
#end
#implementation
#synthesize array;
self.array = [[NSArray alloc] init];
array = [[NSArray alloc] init];
[self.array objectAtIndex:2]; [array objectAtIndex:2];
#end
Is there a difference between them? Why should I use one over the other?
self.array = foo is shorthand for [self setArray:foo] (i.e. you access the synthesized property methods), while just array = foo directly accesses the instance variable.
In exactly this case, you would create a memory leak with self.array = [[NSArray alloc] init]; since the property will retain it and the reference count would thus be 2 instead of 1. So better would be: self.array = [NSArray array];.
Which one to prefer is almost a matter of taste, but using the properties gives you a few advantages like automatic key-value coding support. It's also an advantage if you someday chose to do implement setArray: yourself so it can do additional stuff when the array is assigned (like reloading a UITableView). On the other hand, it's a little bit slower as it's an additional method call (only matters if called in a loop a lot). But for almost all applications it's better to be correct than as fast as possible. Using properties can make memory management easier for you.
The property "array" is declared to retain on assignment (the retain in the brackets after #property signifies this). Because it has the same name as the "array" instance variable it uses that instance variable as it's backing store.
Effectively calling self.array = [[NSArray alloc] init]; is the same as calling array = [[[NSArray alloc] init] retain];
When you assign to the instance variable directly, not using the property, no action is taken on it, so array simply points to a new instance of NSArray without retaining it.
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