1

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; 
1
  • Your original formatting of final NSLOG output was misleading. Commented May 30, 2012 at 7:43

5 Answers 5

2

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

Sign up to request clarification or add additional context in comments.

6 Comments

This is not correct. If you do @synthesize currentView = _currentView; you can see that when you do something like _currentView=nilthe setter is called. Just override the method -(void)setCurrentView and put a NSLog inside.
??? Really .... I am trying that in ARC and regular memory management and is not the way you say in either of them.
Use the _window on the AppDelegate. Do the following: _window = nil, override the -(void)setWindow:(UIWindow* window)`(you don't have to type everything, Xcode will auto-complete for you) method. Put an NSLog inside and you will see it will enter...
@Jacky Boy No! See my comment to your answer.
This is my code, and I believe you are incorrect, are you sure the accessor is not being called by some other object? - (void)setWindow:(UIWindow *)window { NSLog(@"%s called!! ", __PRETTY_FUNCTION__); ... } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSLog(@"putting nil"); _window = nil; NSLog(@"accessor was called?"); NSLog(@"putting the window object"); self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; NSLog(@"accessor was called?"); ... } and the accessor is indeed NOT called.
|
1

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

Comments

0

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.

14 Comments

He also need to use self.currentView = v1 instead of _currentView = v1; to get the object retained, otherwise the same thing will happen: crash.
I am assuming he did @synthesize currentView = _currentView; in which case he doesn't need to do self.currentView he only needs to do _currentView.
His question actually has nothing whatsoever to do with the property declaration. He doesn't use the property at all in either code fragment. He's more concerned about why setting v1 = nil makes the following NSLogs start to work.
@Jacky Boy: No. That's a misunderstanding of what @synthesize does. It creates an instance variable, it doesn't magically add memory management when you access the instance variable. ARC would do that, but he's not using ARC.
@JeremyP The first question is why it crashes, and that in turn has to do with if he is retaining the variable or not before he is releasing it. Using the 'retain' option when declaring the property is one way to make the object retained, in combination with synthesize. Synthesize will indeed ("magically", if you wish) implement the methods for getting and setting the instance variable, it is not the declaration of the variable. For a retain property it will add code to *retain the variable when set *release the variable and set it to nil when released.
|
0

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.

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.