Reputation: 97
I have a property :
@property(nonatomic, assign)UIView *currentView;
when I process the follow code, why it will break?
_currentView =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView); ///break here.
NSLog(@"v1 %@", v1);
I think the _currentView
and v1
both point to a same memory. When use v1
to realese the object, and use _currentView
to print the object, it will crash. I can understand this.
But if the add follow line after v1
release and before print _currentView
. I can`t understand the log.
v1 = nil;
the code like follow
_currentView =nil;
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
_currentView = v1;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
v1 = nil;
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
print result is :
> 2012-05-30 15:16:57.314 All[3068:15203] _currentView <UIView:
0x81ccbc0; frame = (0 0; 0 0); layer = <CALayer: 0xa07e5a0>>
> 2012-05-30 15:16:57.798 All[3068:15203] v1 <UIView: 0x81ccbc0; frame =
(0 0; 0 0); layer = <CALayer: 0xa07e5a0>
> 2012-05-30 15:16:59.189 All[3068:15203] _currentView <UIView: 0x81ccbc0; frame = (0 0; 0 0); transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)
> 2012-05-30 15:17:09.042 All[3068:15203] v1 (null)
Why after invoke v1
release, and log _currentView
, it will print
_currentView <UIView: 0x81ccbc0; frame = (0 0; 0 0);
transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)>
Upvotes: 1
Views: 224
Reputation: 3398
You are declaring a property, but you are not using it, you are using the instance variable directly. Also you are failing to retain the memory that your variable is pointing to.
It looks like you have an instance variable declared in your class:
@interface MyClass : NSObject {
UIView * _currentView;
}
@end
What you are doing is that you are accessing this directly, without using the property. What happens is that you are not retaining the memory when you assign it, which means you are releasing it completely and it gets deleted. To make it work this way, you could do this:
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
[_currentView release];
_currentView = [v1 retain]; // <-- OBSERVE the retain
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"_currentView %@", _currentView); // Should not break anymore
NSLog(@"v1 %@", v1);
Later you need to release the object retained in _currentView:
-(void)dealloc {
[_currentView release];
}
(Observe that you also need to do this if you want to assign a new value to _currentView)
Another way would be to actually use the property that you declared, but instead use a retain property:
@property(nonatomic, retain)UIView *currentView;
To make it accessible, you need to synthesize it in your class implementation:
@implementation MyClass
@synthesize currentView = _currentView;
/*...*/
@end
That will make the retain be handled for you. Also you do not need to think about releasing the previous value stored in the variable, since that will be handled for you. However you need to access the property this way:
self.currentView
Your code example would look like this:
UIView * v1 = [[UIView alloc] initWithFrame:CGRectZero];
self.currentView = v1;
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView);
NSLog(@"v1 %@", v1);
[v1 release];
NSLog(@"currentView %@", self.currentView);
NSLog(@"_currentView %@", _currentView); ///Should not break anymore
NSLog(@"v1 %@", v1);
As you would see from the printout, the two variables still point to the same memory. The different is that since you are retaining the memory, a retain counter is added to it. The memory will not be freed until the retain counter has reached 0, which means you need to release it one time for every time you retain it. Also in the later case you need to release the retain in your dealloc method:
-(void)dealloc {
[_currentView release];
}
As for you last question this row
v1 = nil;
will only affect the address to which v1 points to. It would not affect the variable _currentView nor the memory it is pointing to.
Upvotes: 0
Reputation: 86691
Just want to add one point to nacho4d's answer. If you NSLog
a deallocated object, sometimes it will crash and sometimes it won't. When the object is deallocated, all that happens is it gets added back on to the list of free memory blocks. The actual content of the memory still looks like an object and until some or all of the block gets reused, sending messages to it can often work.
One of three things can happen when you NSLog a deallocated object.
Whichever one happens is largely a matter of chance.
Upvotes: 0
Reputation: 45158
This is not necessarily related to the @property
attribute (assign or retain) because you are not using accessors
This is what happens in your code:
@property(nonatomic, assign)UIView *currentView;
You declare an ivar to be assign
although that is irrelevant in this case since you are not using self.currentView
or [self setCurrentView:...];
.
_currentView = nil;
// You just made your pointer _currentView point to nil or 0
UIView *v1 = [[UIView alloc] initWithFrame:CGRectZero];
// You created an UIView object and made v1 to point to it. (v1 is NOT the real object)
_currentView = v1;
// You just made _currentView to point the same object v1 points to
NSLog(@"_currentView %@", _currentView);
// and because of that you correctly see the object here (because _currentView points to the view object)
NSLog(@"v1 %@", v1);
// also here (because v1 points to the object from the start)
[v1 release];
// now you release the object pointed by v1 , since no other is retaining it, it gets deallocated BUT note that v1 is still pointing to it, which now is garbage memory!)
//NSLog(@"_currentView %@ v1 %@", _currentView, v1);
// If above line were executed the app will crash because of v1 and _currentView both are pointing to the object that was just released and it is not valid anylonger.
v1 = nil;
// Now you made v1 to point to nothing so next time you use it terrible things will not happen (★) :)
NSLog(@"_currentView %@", _currentView);
// Oh no! you called _currentView and since it was still pointing to the object you released a bit ago the app crashes :(
NSLog(@"v1 %@", v1);
// This is fine, you set v1 to point to nil so it is not pointing to some garbage memory you simply get nil.
(★) Because in objective-c sending methods to nil
is harmless, using nil as parameters of other methods is another story
Even if you write self.currentView = v1;
instead of _currentView = v1;
results would be the same since the properly is declared as assign.
Things would be different if you declare the property as retain
. In that case after you do [v1 release];
the object will not be deallocated since the object was retained by currentView (self.currentView = v1
). Then if you do v1 = nil
v1 will be pointing to nil and the object will be reachable only by currentView.
Then if you do _currentView = nil
then _currentView will be pointing to nil but the object itself will not be released since you didn't use the accessory method (nor explicitly released) hence you will get a dangling pointer.
Not all the times properties declared as retain are the solution, it is case by case. I recommend to read a bit more about memory management in Obj-c (at least this) also a bit about C pointers and then about ARC
Upvotes: 2
Reputation: 2541
The reason why you get different printouts for second output is following:
After you have executed: [v1 release];
both v1 and _currentView are pointing to old block of memory. However setting v1 = nil;
will set only v1 to nill and not _currentView (remember these are pointers).
I hope this clarifies the things for you.
Kind regards,
Bo
Upvotes: 1
Reputation: 25927
The problem is how you declared the property:
@property(nonatomic, assign)UIView *currentView;
It should be:
@property(nonatomic, retain)UIView *currentView;
When you try to NSLog
, it will have a garbage valor, since you release it previously:
[v1 release];
NSLog(@"_currentView %@", _currentView);
Remember that, at this point, when you try to NSLog
it, v1 and _currentView will be pointing to the same block of memory.
Upvotes: 0