I wanna ask if I allocated an instance variable for private use in that class, should i release it immediately on site, or i can depend on dealloc function. (because maybe i will need it on other function) ?
//Player.h
#interface Player : NSObject
{
NSMutableArray * objectArray;
}
- (void)awake;
- (void)add;
#end
//Player.m
#implementation Player : NSObject
{
-(id) init {
self = [super init];
if (self != nil ){
[self awake];
[self add];
}
return self;
}
- (void) awake {
objectArray = [[NSMutableArray alloc] init]; //is it cause leakage?
[objectArray addObject:#"foobar"];
}
- (void) add {
[objectArray addObject:#"foobar2"];
}
- (void) dealloc {
[objectArray release];
[super dealloc];
}
}
#end
or should i using property to set the objectArray iVar?
//Player.h
#interface Player : NSObject
{
NSMutableArray * objectArray;
}
#property (nonatomic,retain)NSMutableArray* objectArray;
- (void)awake;
- (void)add;
#end
//Player.m
#implementation Player : NSObject
{
-(id) init {
self = [super init];
if (self != nil ){
[self awake];
[self add];
}
return self;
}
- (void) awake {
self.objectArray = [[NSMutableArray alloc] init autorelease]; //cause leakage?
[objectArray addObject:#"foobar"];
}
- (void) add {
[objectArray addObject:#"foobar2"];
}
- (void) dealloc {
[objectArray release];
[super dealloc];
}
}
#end
if both of them doesn't cause a leakage, what type should i use?
should i always set iVar property, and access iVar value with self even if i only want to use it in this class?
I like to take the stance that if the instance variable should not be visible outside of the class then it should not be implemented as a property. But it's a personal thing that other developers may not agree with.
Either way you would need to release the objectArray in your classes dealloc method - which is what you're currently doing.
However you need to be careful with your awake method - if it's invoked multiple times then objectArray is leaked. This is the downside of not using properties. A use of self.objectArray = [[NSMutableArray alloc] init] here would have released the previous object.
In my opinion, you should only declare properties in your header if other objects are allowed to use them. There is no good reason why you would provide an -add: method (as in your example) that adds something to your array while also providing a getter for your array so other objects can manipulate it directly. It's called encapsulation.
If you do want to have the benefits of generated getters/setters for your implementation file, you can always use a class continuation (a nameless category) inside your implementation file and include your property declarations there. That way you get real, auto-generated properties that are only visible to your class' implementation.
Personally, I wouldn't use any getter or setter methods in your example. Just allocate the NSArray in your -init and release it in -dealloc. If this -awake method of yours might be called multiple times, just add an [objectArray removeAllObjects] call and you're sure to have an empty array without worrying about memory management.
It is very likely that memory will leak in your first example because you are not sending release to the previously set instance variable (if it already existed).
This is why you should use property setters - they handle all of this stuff for you.
Also, since you are obtaining ownership of the instance variable through the property (which is defined with the retain keyword), you will definitely leak memory if you don't send the instance variable the -release message in your -dealloc method.
So the verdict is that you should use the second example, not the first.
Related
This question already has answers here:
app crash in the dealloc
(2 answers)
Closed 9 years ago.
I'm facing small problem,
I declare an array in .h file and allocate it in viewDodLoad method. In dealloc method I check if array not equal to nil then array=nil. But it's crashing in iOS 5.1.1. I can't understand reason for this crash.
My code,
#interface SampleApp : UIViewController
{
NSMutableArray *objArray;
}
#end
#implementation SampleApp
- (void)viewDidLoad
{
[super viewDidLoad];
objArray=[[NSMutableArray alloc]init];
}
-(void)dealloc
{
[super dealloc];
if (objArray!=nil)
{
[objArray removeAllObjects];
[objArray release];objArray=nil;
}
}
Add [super dealloc]; at the end of dealloc method and not at beginning. It is recommended by Apple in its documentation for dealloc method.
When not using ARC, your implementation of dealloc must invoke the superclass’s implementation as its last instruction.
Modify your code as below,
-(void)dealloc
{
if (objArray!=nil)
{
[objArray removeAllObjects];
[objArray release];objArray=nil;
}
[super dealloc];
}
Also, you need not call [objArray removeAllObjects] when you are releasing the entire array. When array is released, internally it will call release on all contained objects.
Hope that helps!
[super dealloc] method must be call in end of this method. Because you can not access variables of the superclass anymore because they are released when you call [super dealloc]. It is always safe to call the superclass in the last line.
-(void)dealloc
{
// ----------- your stuff ------------
[super dealloc];
}
With manual memory management, your -dealloc method looks as follows:
-(void)dealloc
{
[objArray release]; // objArray *may* be nil, and this is
// sufficient to release all elements as well.
// call super at the end
[super dealloc];
}
Additionally, you have a potential memory leak in your method -viewDidLoad. If you do it like your example:
- (void)viewDidLoad
{
[super viewDidLoad];
objArray=[[NSMutableArray alloc]init];
}
you may assign objArray a new pointer even if objArray already holds a valid object. The new pointer value will simply override the old, and thus you cannot release the old anymore.
One way is to check whether objArray is not nil and then release it before assigning it a new value:
- (void)viewDidLoad
{
[super viewDidLoad];
if (objArray) {
[objArray release], objArray = nil;
}
objArray = [[NSMutableArray alloc]init];
}
The better approach however is to employ "lazy initializing properties":
First, define an "internal property" for your array (unless you want the array publicly accessible). In your .m file:
// In your implementation file define a private property in a class extension:
#interface SampleApp ()
#property (nonatomic) NSMutableArray* objArray;
#end
#implementation SampleApp
#synthesize objArray = _objArray; // this will create the setter
-(void)dealloc
{
[_objArray release];
[super dealloc];
}
// Lazy init property: (this is the getter)
- (NSMutableArray*) objArray {
if (_objArray == nil) {
_objArray = [[NSMutableArray alloc] init];
}
return _objArray;
}
- (void) viewDidLoad {
[super viewDidLoad];
// When needing the array, simply *and always* access it
// through the property "self.objArray":
NSUInteger count = [self.objArray count];
}
...
Lazy initialization of properties are quite handy. Basically, you won't worry anymore if they are initialized or not - they simply are, when you use the property accessor.
So I'm trying to write an initializer for a subclass of NSOperation. This is my first time using NSOperation. My NSOperation subclass looks like this:
.h
#property (nonatomic, copy) NSString *fileName;
.m
#synthesize fileName = _fileName;
- (id)initWitFileName:(NSString *)fileName {
if (self = [super init]) {
_fileName = fileName;
}
return self;
}
- (void)dealloc {
[_fileName release];
[super dealloc];
}
- (void)main {
// do long task
}
So I thought when I create my own initializers, I should just set the ivar myself since the state of the object is undetermined in the init method. So I don't use the accessor in the initializer. When my main method gets run, I get a memory bad access and a crash. However, in my init method, if I do this instead:
- (id)initWitFileName:(NSString *)fileName {
if (self = [super init]) {
_fileName = [fileName retain];
}
return self;
}
I do not get a crash. What is correct? I'd think in this second case that I wouldn't be releasing the memory since the accessor is (copy). Or is it because I don't use the accessor, that my fileName object basically gets dealloc'd only since there is no +1 in the initWithFileName method? Thanks!
The second one is close, but:
- (id)initWitFileName:(NSString *)fileName
{
if (self = [super init]) {
_fileName = [fileName copy];
}
return self;
}
is more usual since NSString* properties are generally defined with (nonatomic, copy).
This is done to avoid problems or unintended consequences if the caller is actually passing a NSMutableString and later changes the value.
When you #synthesize something with a set of properties, you're generating getters and setters for the object. This allows you to use methods like
[self setFilename:#"MyFile.txt"]
or
self.filename = #"MyFile.txt"
Both of the lines above function in exactly the same way, and might be implemented like this:
- (void)setFilename:(NSString *)string {
_filename = [string copy];
// Or, for a retained property:
// _filename = [string retain];
}
This implementation is overly simplified. See idz's comment below for a better understanding of the logic that a real setter would use.
In your example above, you're simply using the = operator, and not a generated function.
_fileName = fileName;
Because of this, you're simply setting the address of _fileName to fileName. None of your synthesize properties matter, and don't make a difference here.
Your explanation:
Is it because I don't use the accessor, that my fileName object basically gets dealloc'd only since there is no +1 in the initWithFileName method?
is exactly correct. To properly replicate the copy property used by your synthesizer, use this:
_fileName = [fileName copy];
Idea 1: Use ARC. In ARC, you'd declare your ivar strong, and then either use the synthesized setter or assign the ivar directly. A retain will be added for you and all will be well.
Idea 2: Use retain. If you're unable to use ARC for some reason, why you don't declare the NSString as retain, instead of copy? It's okay to hold onto the same instance of an immutable thing. (Sharing a mutable thing can be useful, too, as long as both classes understand that they are doing that).
Idea 3: If you must use non-arc and you must use copy, set the ivar with copy. That will fix your crash by doing the copy. As it is now, the code is copying the handle to the passed string, but not it's contents. The passed string is dealloc'd and your class is stuck with a zombie. You can say _filename = [string copy]; as others have suggested.
As a side-note, calling self = [super init]; is correct inside the overridden init method. Since you're making your own init variant, I think it's more proper to call self = [self init];
Let say i have an class named as MyTestClass.h.
Class structure is look like
#interface MyTestClass : NSObject {
NSString *testString;
}
#property (nonatomic, retain)NSString * testString;
#end
.m file
#implementation MyTestClass
#synthesize testString;
-(id) init{
[self setTestString:#""];
return self;
}
-(void)dealloc{
[self.testString release];
testString = nil;
[super dealloc];
}
#end
Now i created an object of MyTestClass and assigned testString twice
MyTestClass * myTestClass = [[MyTestClass alloc] init];
[myTestClass setTestString:#"Hi"];
[myTestClass setTestString:#"Hello"];
Now i think, two times my testStrings memory is leaked!! (one through init() and another one through my first setTestString method)
Am i correct? or will #property (nonatomic, retain) handle/release previous allocated memory?
or ,in this kind of cases ,will i need to override the setTestString() in MyTestClass.m like below code
-(void)setTestString:(NSString *)tempString{
[testString release];
testString = nil;
testString = [tempString retain];
}
Any help on this question is appreciated.
Thanks.
Any help on this question is appreciated.
I'll take this as a licence to make sone observations not necessarily directly related to your question.
Firstly, if you declare a retain property (as you have done) and synthesize it, the automatically generated getters and setters handle memory management correctly for you.
If you manually create setter (which you are allowed to do even with an #synthesize existing), you have to do the memory management yourself. Use either of trojanfoe's examples.
The setter in your question contains a bug in that if testString == tempString i.e. you assign the value of the property to itself, you could end up with assigning a dangling pointer to the property because you effectively release tempString and then retain it.
This is an implementation detail that you an safely ignore, but string literals e.g. #"blah" are compiled into the executable and will never be deallocated no matter how many times they are released. So, with your example, even if the setter did not do correct memory management, there will be no leak.
By the way, the normal pattern for an init method is
-(id) init
{
self = [super init];
if (self != nil)
{
// init stuff
}
return self;
}
or logical equivalent.
You should get into the habit of using it because you need to call the super class's init method and it is allowed to change the value of self, even to nil.
Also, while it is very good practice normally to set the object reference to nil after releasing it, in both cases when you do it, it is unnecessary. the first time, the variable is about to go out of scope and the second time you immediately assign it from some other object.
It's not a leak. Synthesized variable are correctly handled.
A synthesized method is implemented in this way (for a retain keyword)
#property (nonatomic, retain) NSString *string;
//backed by variable NSString *_string;
- (void)setString:(NSString*)newString
{
if (newString != _string) {
[_string release];
_string = [newString retain];
}
}
Of course this is a leak:
- (void)aMethod //of my class with string property
{
NSString *aString = [[NSString alloc] initWithString:#"hello"];
self.string = aString; //retain count of 2
self.string = #"hello2"; //retain count of 1 for aString
//now I don't release aString.... leak
}
If you use the auto-generated setter (in your case, setTestString:, which is also called by self.testString = ...;), the previous value of a retain property is released before being set. So no, there is no leak in the code you posted above.
The synthesized setter method should do the right thing. Here's an example of it's implementation:
- (void)setTestString:(NSString *)tempString
{
[tempString retain];
[testString release];
testString = tempString;
}
or:
- (void)setTestString:(NSString *)tempString
{
if (tempString != testString)
{
[testString release];
[tempString retain];
testString = tempString;
}
}
the dealloc is only called when the instance is destructed.
if you do :
[myTestClass setTestString:#"Hi"];
[myTestClass setTestString:#"Hello"];
in the same block, you're juste calling twice the setter. there is no memory leak.
When you use #synthesize on a property that specifies retain, the setter that's generated will handle the retain/release correctly for multiple assignments. As long as you use self. rather than going directly to the backing variable and do a final release in dealloc you should be fine.
Why doesn't this common property initialization scheme risk failure when the synthesized setter tries to release the undefined myArray object? Or are property objects automatically initialized to nil and I don't need to be doing this at all?
#interface myClass : NSObject {
NSArray* myArray;
}
#property (nonatomic, retain) NSArray* myArray;
#end
#implementation myClass
#synthesize myArray;
-(id)init {
if ( self = [super init] ) {
self.myArray = nil;
}
return self;
}
...
Object instance variables in Objective-C are initialized to nil by default. Furthermore, messaging nil is allowed (unlike calling a method on null in function-calling languages like Java, C# or C++). The result of a message to nil is nil, this calling [nil release]; is just nil, not an exception.
On a side note, it's best practice to assign/call instance variables directly in -init and -dealloc methods:
-(id)init {
if ( self = [super init] ) {
myArray = nil;
}
return self;
}
- (void)dealloc {
[myArray release];
[super dealloc];
}
As others have stated, the instance variable is already initialised to nil.
Additionally, as per Apple's documentation, instance variables should be set directly in an init method, as the getter/setter methods of a class (or subclass thereof) may rely on a fully initialised instance.
It's already initialized to nil.
Is there a reason, other than as may be required by object scope across different methods of the object, to use an object like this:
#interface
...
ViewMgmtAppDelegate : NSObject <UIApplicationDelegate> {
Obj *obj1;
...
}
#end
#implementation
- (void)applicationDidFinishLaunching:(UIApplication *)application {
obj1 = [Obj alloc];
[window addSubview:obj1];
...
}
- (void)dealloc {
[obj1 release];
...
}
#end
Instead of this:
#interface
...
ViewMgmtAppDelegate : NSObject <UIApplicationDelegate> {
...
}
#end
#implementation
- (void)applicationDidFinishLaunching:(UIApplication *)application {
Obj obj1 = [Obj alloc];
[window addSubview:obj1];
[obj1 release];
...
}
- (void)dealloc {
...
}
#end
Is either way more efficient?
Any help appreciated // :)
I assume you are asking if you should release the object right after addSubview or in dealloc.
It depends on the application logic and what addSubview does. If addSubview retains the object you can release it right after the call if you don't need it in your other object.
When window is done with it and it releases the object which will get deallocated if no other object holds reference to it.
If on the other hand you keep the reference until your object is deallocated then the object will hang around until your object is deallocated.
If you don't need it then release right away.
By the way the correct way to create and instance is to call:
Obj * o = [[Object alloc] init]
Alloc allocates memory, init (or any other init... methods) initializes the object (I believe).
You would use your first example in case that your obj1 will be used after the view finished loading. For example after a click event.
The second example will be used if this is not the case. So Obj1 is not needed anymore after the release and does not need to use the RAM of the iPhone.
If the object is only used by that method, then it should be a local variable and released in that method (as in the second example). If it's a property of the class that other methods use, it should be stored in an instance variable and released when the owning object goes away (as in the first example).