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)>
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
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
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.
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.
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.
Related
[EDIT: Since it was causing confusion, this whole case assumes MRR and not ARC]
I'm having an odd (there's an explanation obviously, I just can't figure it out) behavior with a block which references self (indirectly) and is in turned copied to another object's property (that is, copied from object As' stack to the heap and retained by an object B). If the block doesn't contain the reference to _this, Object A's dealloc is called every time it is popped from a navigation controller, as it should. However, if the block references _this, the object's (MyObjectA in the code below) dealloc is only called every other time. That is, I push this view controller subclass, createBlock is called, I pop and nothing happens. I push again, createBlock is called again, then pop and then it DOES call dealloc on MyObjectA.
For the sake of brevity, I'm only posting the snippets I believe are key to the behavior.
Say I have an object MyObjectA (sublcass of a custom UIViewController), which includes a method createBlock, like so:
- (void)createBlock
{
__block MyObjectA* _this = self;
int(^animationBlock)(NSArray*,NSDictionary*);
animationBlock =
^(NSArray* layers, NSDictionary* parameters)
{
[CATransaction begin];
[CATransaction setCompletionBlock:
^{
for(CALayer* layer in layers)
layer.opacity = 1;
}];
CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:#"opacity"];
a2.fromValue = [NSNumber numberWithFloat:0.];
a2.toValue = [NSNumber numberWithFloat:1.];
a2.duration = .4;
a2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a2.fillMode = kCAFillModeBoth;
a2.removedOnCompletion = NO;
CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:#"position.x"];
a.duration = .4;
a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
a.fillMode = kCAFillModeBoth;
a.removedOnCompletion = NO;
CAAnimationGroup* g = [CAAnimationGroup animation];
g.animations = [NSArray arrayWithObjects:a,a2, nil];
g.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
g.fillMode = kCAFillModeBoth;
g.removedOnCompletion = NO;
CALayer* numberLayer;
CALayer* flechaLayer;
CGFloat timeOffset = 0;
for(int i = 0; i < [layers count]; i+=2)
{
numberLayer = [layers objectAtIndex:i];
flechaLayer = [layers objectAtIndex:i+1];
a2.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;
[numberLayer addAnimation:a2 forKey:nil];
a2.beginTime = 0;
a.fromValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + 100];
a.toValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + flechaLayer.frame.size.width / 2.];
g.duration = 3;
g.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset + .4;
[flechaLayer addAnimation:g forKey:nil];
timeOffset += 1.5;
}
[CATransaction commit];
return 0;
};
[[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:#"EnsureFlechasNutricion"];
}
As you can see, there's a reference to _this in the animation block.
Then, the AnimationFactory's (a singleton) method which registers the blocks is:
- (void)registerAnimationBlock:(int(^)(NSArray*, NSDictionary*))animationBlock forKey:(NSString*)key
{
int(^heapBlock)(NSArray*, NSDictionary*) = [animationBlock copy];
[self.animationBlocks setObject:heapBlock forKey:key];
[heapBlock release];
}
My guess is that copying the block to the heap is retaining MyObjectA, or perhaps adding the block to the NSMutableDictionary in the AnimationFactory.. but I'm not sure.
Any thoughts?
Ok I figured it out: When I add the newly copied (heap) block to the AnimationFactory's dictionary, I'm necessarily retaining self even if doing the weak reference shebang when originally declaring the block in self.
The solution is to get a weak (aka __block Class* identifier = eval since I'm not on ARC) reference to self.view, which is the reason I was referencing self to start with, as opposed to one to self. This way, in spite of this view's reference count increasing, self's ref count stays correct. Then, on pop, self is not retained by the AnimationFactory's dictionary and does call dealloc.
I should mention that self's dealloc includes a call to another method which in turn removes all registered blocks from the AnimationFactory, bringing self.view's retain count back to normal too implicitly, and thus not leaking.
[Update: this answer applies when using ARC and from the comments it turns out MRC is being used, so its not the answer!]
The __block attribute is for when you need a variable which can be updated by a block, i.e. the variable is passed to the block by reference rather than by value as is the default case. This does not appear to be needed in your code, you don't update the value of _this within your block.
To break a strong reference cycle use the __weak attribute. Your current _this is a strong reference to the same object that self references, so your block ends up with a strong reference.
According to the static analyzer if we have the following property:
#property (retain, nonatomic) SomeObject * object;
and then we assign the property like so:
self.object = [SomeObject alloc] init];
a leak occurs. This makes sense because the alloc init adds +1 to the retain count and then the retaining property also increments the retain count. What is the best solution here? typically I just add an autorelease like so:
self.object = [[SomeObject alloc] init] autorelease];
But sometimes this creates problems for me and I end up over releasing the object causing my app to crash. I don't have any specific examples right now but I remember I had to take out some autoreleases cause of the application crashing. Is there something I am missing here?
EDIT: I have a concrete example now of the issue I was running into.
NSMutableArray *newData = [NSMutableArray array];
//If this is true then we are showing all of the items in that level of hierarchy and do not need to show the summary button.
if (!(contextID.count >= 1 && [[contextID objectAtIndex:contextID.count - 1] isEqual:[NSNull null]]) && contextID.count != 0)
{
GeographyPickerItem * firstItem = [[GeographyPickerItem alloc] init];
firstItem.primaryString = [NSString stringWithString:#"Summary"];
firstItem.subString = [NSString stringWithString:#""];
firstItem.isSummaryItem = YES;
[newData addObject:firstItem];
[firstItem release]; //TODO: Figure out why this is causing EXC_BAD_ACCESS errors
}
self.hierData = newData;
The code above is in the init method of a viewcontroller. HierData is a retained property, which is released in the viewControllers dealloc method. GeographyPickerItem retains the two strings, primaryString and subString and releases them in its own dealloc method. My application crashes (sometimes) when the viewControllers are de-alloced following a pop off of a navigation controller. It crashes with a EXC_BAD_ACCESS signal in the dealloc method of GeographyPickerItem (either on [substring release] or [primaryString release]).
I don't understand why this is happening because I believe I am following proper memory management guidelines. If I comment out firstItem release everything is fine.
The autorelease method you mention is fine, as is the other common idiom of:
SomeObject *thing = [[SomeObject alloc] init];
self.object = thing;
[thing release];
If you end up overreleasing later on, that is your problem. This part, which you're apparently doing correctly, is not the problem.
SomeObject * new_object = [SomeObject alloc] init];
self.object = new_object;
[new_object release];
or use ARC
check the GeographyPickerItem, if the strings properties are assign (and change to retain), or check if you always initialize them (before release).
also remember the difference of manually allocating :
[[NSString alloc] initWith...]
You must release or autorelease.
[NSString stringWith...]
No need to release.
or use ARC like meggar said
Turns out the issue was simple, my dealloc method called super dealloc at the start of the method rather than at the end. You always have to release your instance variables before you call [super dealloc]!
This has to be very basic, but I don't see the problem. The program crashes whenever the following code block is executed. Analyzer reports a possible memory leak:
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
The message is -[UIImage release]: message sent to deallocated instance 0x1129d920*
Instance 0x1129d920 is scaledImage
I tried adding retains and releases, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [[anImage scaleToFitWithin:defaultEventImageFrame.size interpolationQuality:kCGInterpolationHigh] retain];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
[scaledImage release];
}
and still get the error message.
So I tried replacing the assignment with a copy, like this
if (anImage) {
eventImageView.frame = defaultEventImageFrame;
UIImage *scaledImage = [anImage copy];
eventImageView.backgroundColor = [UIColor colorWithPatternImage:scaledImage];
}
And the problem is gone.
Checking the scaleToFitWithin method, I see it returns an autoreleased object:
- (UIImage *) scaleToFitWithin:(CGSize) newSize
interpolationQuality:(CGInterpolationQuality)quality{
CGSize originalImageSize = self.size;
CGSize newImageSize;
if (originalImageSize.width <= originalImageSize.height) {
newImageSize.width = self.size.width * newSize.width / self.size.width;
newImageSize.height = self.size.height * newSize.width / self.size.width;
}
else {
newImageSize.width = self.size.width * newSize.height / self.size.height;
newImageSize.height = self.size.height * newSize.height / self.size.height;
}
return [[[self normalize] resizedImage:newImageSize interpolationQuality:kCGInterpolationHigh] autorelease];
}
So there is something about memory management that I'm not understanding. What is the problem likely to be?
The problem is most likely that the scaleToFitWithin:interpolationQuality: method is calling autorelease on an object which has already previously been autoreleased. This can occur if you initialise the UIImage using a temporary constructor, like +[UIImage imageWith...], earlier in the same method you call the scaling method from. The reason it works when you use [anImage copy] is because the behaviour of the copy constructor is such that the object returned to you has already had retain called on it (so it has a local retain count of 1 and zero autoreleases).
What happens at the end of the current run loop is: the autorelease pool which is currently in use is drained, and as a part of that two release messages will be sent to the UIImage. When the first one is sent, the application then runs off and calls dealloc on the image, because the retainCount has decreased to zero. When the second one is sent, the application throws an exception because a message is being sent to an object which was previously deallocated.
Try removing the autorelease message from the scaleToFitWithin:interpolationQuality: method. Even if your resizedImage:interpolationQuality: method IS returning a new object, you should only be calling autorelease in that method rather than the scaling method.
It seems that the method resizedImage:interpolationQuality: itself returns an autoreleased object and you are again autoreleasing it in the reutun statement. Just remove the autorelease from the return statement,
return [[self normalize] resizedImage:newImageSize
interpolationQuality:kCGInterpolationHigh];
Then you don't have to retain/release or copy the returned object in if (anImage) {...} block.
What if I want to alloc a class inside another and I want to reference it easily, but sometimes this class would not need to be alloc'd, therefore not dealloc'd. How is this done? Can I put a conditional inside dealloc so it doesn't have to be released?
In more detail, I'm using Cocos2D. I have player ability classes that may or may not need to be allocated. In my init:
// Abilities
if(abilityRushH == 0){
objects = [theMap objectGroupNamed:#"oj"];
startPoint = [objects objectNamed:#"item_ability_rushH"];
x = [[startPoint valueForKey:#"x"] intValue];
y = [[startPoint valueForKey:#"y"] intValue];
rushH = [[RushHorizontal alloc] init];
[self addChild:rushH.rushHSpriteSheet];
rushH.rushHSprite.position = ccp(x,y);
}
if(abilityRushV == 0){
objects = [theMap objectGroupNamed:#"oj"];
startPoint = [objects objectNamed:#"item_ability_rushV"];
x = [[startPoint valueForKey:#"x"] intValue];
y = [[startPoint valueForKey:#"y"] intValue];
rushV = [[RushVertical alloc] init];
[self addChild:rushV.rushVSpriteSheet];
rushV.rushVSprite.position = ccp(x,y);
}
Cocos2D needs to keep the reference so it can scroll with the map. But if I'm not alloc'ing it, how do I NOT dealloc?
Since you are talking of releasing it in dealloc, there will be an instance variable for this. Now when any instance of an Objective-C class is allocated, all its objects are nil and c types are set to 0 (or equivalent values). So you don't need to put any extra effort if the object of your class isn't instantiated as the instance variable will be nil at dealloc and so release message sent to it will have no effect.
Make sure the optional variable is nil when its not needed, and do a nil check before dealloc'ing.
How to release this variable with no EXC_BAB_ACCESS ?
//First line create memory leak
UIImage *ImageAvatar = [[UIImage alloc] initWithData:[myg.imageData copy]];
Moins1 = ImageAvatar;
//[ImageAvatar release]; if i release-> EXC_BAD_ACCESS
Moins1 is a menber of the interface is declared like this :
UIImage *Moins1;
...
#property (nonatomic, retain) UIImage *Moins1;
It looks like the problem isn't the UIImage, but rather the NSData. In Cocoa, any copy (or mutableCopy) method returns an object with a +1 retain count, meaning that you own it and are therefore responsible for releasing it.
In your code, you're calling -copy on myg.imageData, but never releasing it. That's a classic example of a memory leak. Here's what I would do to fix it, plus with changing your syntax a bit:
ivar:
UIImage *Moins1;
#property (nonatomic, retain) UIImage *Moins1;
implementation:
NSData * imageData = [myg.imageData copy];
UIImage * ImageAvatar = [[UIImage alloc] initWithData:imageData];
[imageData release];
[self setMoins1:ImageAvatar];
[ImageAvatar release];
You should not need to send -copy to the NSData object. UIImage does not keep a reference to the data around, it just reads it and produces an image. Sending -copy without -release is a memory leak.
However, that does not explain the EXC_BAD_ACCESS. Something else is going on, and not from the code you've posted.
There are two problems in your code. The copying of imageData as indicated by the other contributers, and the assignment to Moins1 field without retaining the object.
The assingment to Moins1 access the field directly, so you would need to do your own retaining. If you don't retain it and release it in the next line, then any subsequence access to the field results into a protection error.
You can use the property for assignment:
UIImage *ImageAvatar =
[[UIImage alloc] initWithData:[[myg.imageData copy] autorelease]];
self.Moins1 = ImageAvatar;
[ImageAvatar release];
Or you can also just do it in one line:
self.Moins1 = [UIImage imageWithData:[[myg.imageData copy] autorelease]];
-(void )dealloc
{
if(self.Moins1!=nil)
{
self.Moins1 = nil;
}
}
see when u give and object a retain property its count is already 1 and when u allocate it its count becomes 2 so when the dealloc is called it will check if its nil and if its not nil make it nil .In this way it will give the retain count of the variable to 0