Leonardo zhang
Leonardo zhang

Reputation: 97

Objective-c pointer

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 &lt;UIView: 0x81ccbc0; frame = (0 0; 0 0);
 transform = [0, 0, 0, 0, 0, 0]; alpha = 0; layer = (null)&gt;

Upvotes: 1

Views: 224

Answers (5)

jake_hetfield
jake_hetfield

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

JeremyP
JeremyP

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.

  • It can log as if it were still alive
  • A completely different object might respond if it starts exactly at the same place
  • You get EXC_BAD_ACCESS

Whichever one happens is largely a matter of chance.

Upvotes: 0

nacho4d
nacho4d

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

Another thing:

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

Bo.
Bo.

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

Rui Peres
Rui Peres

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

Related Questions