CALayer layer not autoreleased - ios

I've got a question regarding the following code:
#interface ViewController ()
#property (nonatomic, weak) CALayer *someLayer;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.someLayer = [CALayer layer];
self.someLayer.frame = CGRectMake(10.0, 10.0, 100.0, 100.0);
self.someLayer.backgroundColor = [UIColor yellowColor].CGColor;
[self.view.layer addSublayer:self.someLayer];
}
#end
From my understanding [CALayer layer] should return an autoreleased value, which should live as long as the method call is in progress. It is then referenced by the weak property someLayer.
Since the layer is later retained by the view's layer everything should be fine and the layer should appear.
What it does, but only on devices < iPhone 5S. At least when I run the code in the simulators.
On newer devices the layer won't be displayed.
Changing the property attribute to strong solves the problem, but I'd like to know why the layer gets released immediately.
Do you have any hints how I can debug this behaviour to see what has changed?
Thanks!
Update:
Using self.someLayer = [CALayer [alloc]] init] gives an appropriate warning
which I understand because when using alloc the value is owned by the caller. Since the owner does not retain the value there's no strong reference.
An convenience initializer like layer should autorelease an retained value, which should be accessible until the autorelease pool is drained. Also there's no warning by Xcode in this case.
Update #2:
It gets more interesting...
I tested the code on an iPhone 6 with iOS 8 and on an iPhone 4 with iOS 6.
The layer won't be displayed on neither of them...
BUT
if I set a breakpoint at the line of the creation and step over it, the layer will be visible on simulators < iPhone 5S and on the devices.
How could I check what's going on under the hood?
Update #3 and some more explanation:
There is a great article on Big Nerd Ranch about this behaviour ARC Gotcha - Unexpectedly Short Lifetimes
A look at the disassembly for the code above shows how ARC inserts _objc_retainAutoreleasedReturnValue
From the article:
you can see ARC's "avoid the autorelease pool" optimization is being used: if a method returns an autoreleased object, and the caller doesn't otherwise need to hang on to it, ARC can avoid the trip to the autorelease pool. The method will return a retained object and objc_retainAutoreleasedReturnValue will dispose of it.
So, to avoid the problem, either declare a strong property (as I mentioned above) or have a look at #graver's answer.

Since your someLayer property is weak there's nothing to hold a strong reference to your layer. You should change your code like this:
...
CALayer *layer = [CALayer layer]; // by default layer this is __strong, so layer holds a strong reference until the end of the scope
[self.view.layer addSublayer:layer]; // Now self.view.layer retains the layer
self.somelayer = layer; // weak reference it
self.someLayer.frame = CGRectMake(10.0, 10.0, 100.0, 100.0);
self.someLayer.backgroundColor = [UIColor yellowColor].CGColor;
// In the end of the scope layer would be released, but it's also retained by self.view.layer and weak referenced by self.someLayer
...

#graver 's code works for me on a real iPhone 6 device, which means layer is showed, you should always rely on real devices instead of simulators. You can refer to this question and xcode debugger hide issue with weak proper, it seems that the debugger would hold a strong refrence to the autorelease object, so it is kept and it is showed.
You can always test self.somelayer by logging it out.
self.someLayer = [CALayer layer];
NSLog(#"someLayer is %#",_someLayer);
It should log as Null, but on some simulators it would has value, which shouldn't happen.

Related

Initial particles from CAEmitterLayer don't start at emitterPosition

My goal is to make an explosion-like animation where many particles are emitted in a short duration. My problem is that CAEmitterLayer, when it begins emitting, adds "future" particles to make it looks like the animation has been running for a while.
How can I disable or workaround this? When I increase the birthRate, I only want particles to start appearing from the emitterPosition, and not at all points along the CAEmitterCell's projected lifetime. Any help is appreciated.
#import "EmitterView.h"
#interface EmitterView ()
#property CAEmitterLayer* emitter;
#end
#implementation EmitterView
- (void) awakeFromNib {
[super awakeFromNib];
self.emitter = (CAEmitterLayer*)self.layer;
CAEmitterCell* snowflake = [CAEmitterCell emitterCell];
snowflake.contents = (id)[[UIImage imageNamed: #"snowflake"] CGImage];
snowflake.lifetime = 3;
snowflake.birthRate = 1;
snowflake.velocity = 50;
snowflake.emissionRange = 3.1415;
self.emitter.birthRate = 0;
self.emitter.emitterCells = [NSArray arrayWithObject: snowflake];
self.emitter.emitterPosition = CGPointMake(100, 100);
self.emitter.emitterSize = CGSizeMake(0, 0);
self.emitter.emitterShape = kCAEmitterLayerPoint;
}
+ (Class) layerClass {
return [CAEmitterLayer class];
}
- (void) burst {
self.emitter.birthRate = 10;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
self.emitter.birthRate = 0;
});
}
#end
This behavior actually changed with the release of iOS 7 and apparently hasn't gone back since then. It used to behave the way you would expect back before iOS 7, but either Apple introduced a bug that caused this, or they chose to change the behavior without telling anybody.
I filed a bug for this exact issue on August 28, 2013, which was closed as a duplicate of another bug report for the same issue. Apple's bug reporter is reporting that the other bug is still open, which means Apple hasn't addressed it yet, despite more than a year and a half to take care of it.
I recommend filing your own bug report with Apple, giving them a simple app that demonstrates the behavior, and maybe getting some renewed attention to it will help get them to do something about it.
EDIT:
After researching the issue a little, I found out that the solution is to add this line:
self.emitter.beginTime = CACurrentMediaTime();
It's important to know that CAEmitterLayer is a subclass of CALayer, which conforms to the CAMediaTiming protocol. Why this whole fact isn't better documented is beyond me.
Note that you probably want to call this from your - (void)burst method, but that if you call it again within a short period of time, while previous particles are still around, you might possibly see some odd behavior because of resetting the beginTime.

UIImageView automatically removing itself from self.view

tutorialImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Tap to Start.png"]];
tutorialImage.frame = CGRectMake(0, 0, 1024, 768);
[tutorialImage addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(blankMethod)]];
tutorialImage.userInteractionEnabled = YES; // i use this line and the previous line so that the user can't press any buttons behind the image
tutorialImage.alpha = 0;
[self.view addSubview:tutorialImage];
[self.view bringSubviewToFront:tutorialImage];
[UIView animateWithDuration:1.0f animations:^{
tutorialImage.alpha = 1;
} completion:^(BOOL finished) {
[self.view addSubview:tutorialImage]; // this line makes the image come back
}];
I know you probably won't be able to deduce the problem just from this code, but is there anything in that code that makes the tutorialImage auto remove itself from it's superview?
Anyway, during the UIView animation the image fades in for a bit like normal, then it disappears. If I add that last line of code there (the commented one), the UIView animation will make the image fade in and flash once halfway through. I just added this image and there is no code telling it to remove itself from superview.
Let me know if you have any ideas as to fixing the problem or showing you more code, I'll check frequently.
Also, I've tried restarting the simulator which didn't work, and the tutorial image is declared in the h file UIImageView *tutorialImage;. The console doesn't show any errors or anything when the problem occurs or anything.
Edit:
Ok, strange. I altered the declaration in the H file from UIImageView *tutorialImage; to #property (strong, nonatomic) UIImageView *tutorialImage; then used _tutorialImage fixed the problem. Is this something to do with the strong parameter? I'll mark who ever can explain what was going on as correct.
When you have a weak reference, ARC will dealloc the object once there are no more retains on it (when no object is pointing at the object with a strong pointer). When you changed the #property to strong, you are now telling ARC to keep the object around until the parent (your view controller) is dealloc'ed.

getting EXC_BAD_ACCESS when installing the app in device, core graphic

What i am doing is to draw a simple rectangle and set color for the painted area
// Just added
#interface Gradient () {
CGColorRef lightBlueColor;
}
#implementation Gradient
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor lightGrayColor];
NSLog(#"frame is %#",NSStringFromCGRect(self.frame));
NSLog(#"bound is %#",NSStringFromCGRect(self.bounds));
lightBlueColor = [UIColor colorWithRed:105.0f/255.0f green:179.0f/255.0f blue:216.0f/255.0f alpha:1.0].CGColor;
}
return self;
}
-(void) layoutSubviews {
paperRect = CGRectMake(10, 10, self.bounds.size.width/2, self.bounds.size.height/2);
}
-(void)drawRect:(CGRect)rect {
//Draw a retangle
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, lightBlueColor);
CGContextFillRect(context, paperRect);
}
Below is what is displayed on the simulator
However, when I am trying to install on the device, I am getting EXC_BAD_ACCESS
Question :
why it does not work on the device. Did I make a mistake somewhere
Edit : I have just tried to modified lightBlueColor
lightBlueColor = [UIColor blueColor].CGColor;
Then I can run the app on the device. I dont get it at all
Edit: so you just ran into a memory management issue. In theory, in both of the cases, the program could have crashed. Why? Because as per the documentation, -[UIColor lightBlueColor] and its accompaining class methods return an autoreleased object. In practice, for optimization reasons, it's not true: in fact, they return the same alloc-initied singleton in order to save memory. This optimization is possible because they always return the same color. As the shared instance (singleton color) has been created without an autorelease, it doesn't suddenly get released when you wouldn't expect that, that's why the program worked. If Apple's programmers weren't smart enough to make this optimization, both programs would have crashed.
But in the second case, the colorWithRed:green:blue:alpha: method cannot possibly have this optimization, since it's not guaranteed that it always returns the same color. (Imagine what would happen if you called it to obtain a red color first, then it cached it, then you wanted to obtain a blue color, but it would have returned the cached red one.) So it actually creates a new instance of a color and autoreleases it. But since you don't retain it, soon it gets deallocated because of the autorelease and thus its CGColor property is invalidated as well. So, there are three possible solutions:
One. I'd prefer this. Make the lightBlueColor instance variable an UIColor object and create it using
lightBlueColor = [[UIColor alloc] initWithRed:r green:g blue:b alpha:a];
then just use its CGColor property for drawing.
Two. Similar to the first one, but you can create the color object as
lightBlueColor = [[UIColor colorWithRed:r green:g blue:b alpha:a] retain];
as well, however I'd say this is a wrong concept.
Three: you can make the UIColor object vanish in the deep well of autorelease pools, but keep the CGColor safe:
lightBlueColor = CGColorRetain([UIColor colorWithRed:r green:g blue:b alpha:a].CGColor);
In each case, you should pay attention to memory management in the dealloc method.
Lesson learned: instance variables aren't good for storing autoreleased objects. You want to alloc-init and release when appropriate.
Your issue appears to be one of memory management. You don't call CGColorRetain on lightBlueColor in your init method. It works for UIColor blueColor most likely because that color is some constant kept around by the OS. Your color isn't so it goes out of scope and gets deallocated.
Once you add that, don't forget to call CFColorRelease in dealloc or any other appropriate place as needed.

iOS: Why is drawRect: being called twice, and why does this ivar value seemingly change without any reason?

I have created a UIView subclass in order to implement a custom drawRect method. By putting some logs in the code I found that drawRect is actually getting called twice as the view first gets set up by its view controller. Why is this?
The other issue is that my UIView subclass has an ivar named needsToDrawTools. My designated initializer for this subclass sets the value of needsToDrawTools to YES. The drawRect method then checks this value. If YES, it draws the tools and then sets the value of needsToDrawTools to NO so that it never re-draws the tools.
BUT, somehow the value of needsToDrawTools is NO by the time drawRect is called. Nowhere in my code am I setting it to NO, other than from within the if(needsToDrawTools) statement inside drawRect. But since needsToDrawTools is already NO by the time it reaches that if statement, the code inside the statement never even runs. If I remove that IF statement altogether, then it does run of course and I see what I expect in the view. But I don't want to remove the IF statement because that will result in re-drawing of things that don't need to be re-drawn.
Here's my code:
- (id)initWithParentViewController:(NewPhotoEditingViewController *)vc
{
self = [super init];
if (self) {
parentVC = vc;
needsToDrawTools = YES;
NSLog(#"needsToDrawTools: %i",needsToDrawTools); //Console result: 1
}
return self;
}
#pragma mark - Drawing
- (void)drawRect:(CGRect)rect
{
NSLog(#"needsToDrawTools: %i",needsToDrawTools); //Console result: 0 !!!!!
if (needsToDrawTools){
NSLog(#"drawingTools"); //Never shows up in the console
UIBezierPath *toolPointDragger = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(75, 100, 30, 30)];
[[UIColor blackColor] setStroke];
toolPointDragger.lineWidth = 6;
[toolPointDragger stroke];
[[UIColor blueColor] setFill];
[toolPointDragger fill];
needsToDrawTools = NO;
}
}
So again, my two questions are:
Why is drawRect being called twice? I assume it gets called the first time automatically as part of the view loading process, but I don't know why it then gets called again.
How does needsToDrawTools end up with a value of NO?
It sounds like you've got more than one instance of this view. Perhaps you're creating one programmatically and loading one from a nib? Objective-C will set all ivars to zero (or nil, or NO) when an object is created, and if you're loading an instance of your view from a nib, it won't be initialized with your -(id)initWithParentViewController: and needsToDrawTools should be NO for that view.

objective-c: alloc vs synthesise/retain

I have a beginner's question. I would like to have access to an UIView in all parts of my code (as opposed to a single method/function).
I therefore declared it in the #interface section
UIView *calendarView;
and then #property (nonatomic, retain) UIView *calendarView. In the #implementation I have #synthesise calendarView.
Now I would like to define the frame of this view and coded the following:
CGRect calendarFrame = CGRectMake(170, 8, 200, 50);
calendarView = [[UIView alloc] initWithFrame:calendarFrame];
But I am now wondering if I am doing something seriously wrong here, as calendarView has already been retained and synthesised. Surely UIView alloc is superfluous or even bound to make the app crash, as I am running into memory problems, right?
So I though I should code this instead of the two previous lines, but it only had the effect that the calendarView is not shown at all:
[calendarView setFrame:CGRectMake(170, 8, 200, 50)];
So my question is if I really need to alloc the view before I can use it? Or is there yet another solution?
you can retain an object only after you have it in memory(ie you have alloc'ed it)..So below given code is correct and needed..
CGRect calendarFrame = CGRectMake(170, 8, 200, 50);
calendarView = [[UIView alloc] initWithFrame:calendarFrame];//you have alloc'ed once
now you will add this view to a parentView. For example I am inside a UIViewController implementation..
[self.view addSubView:calendarView]; //now only your retain takes place. //So now calenderView has to release twice..Once by you (since you alloced it) and once by viewcontroller (since it has retained it)...
[calendarView release];//we are releasing once but the object will not be removed from memory since it is retained by viewController..Our part in memory management is over..Now when this viewController get dealloced it releases
You can use this calendarView throughout the implementation of this UIViewController..
-(void)dealloc{
[super dealloc];//should be last in dealloc..Now the entire controller will be dealloced along with the calenderView which is retained by viewController and the memory will be freed for future uses..
}
These are some useful tutorials..easier to understand than apple's documentation..But read Apple's documentation too..
http://ferasferas.wordpress.com/2010/12/05/introduction-to-memory-management-on-iphone/
http://iosdevelopertips.com/objective-c/memory-management.html
http://mauvilasoftware.com/iphone_software_development/2008/01/iphone-memory-management-a-bri.html
https://humblecoder.blogspot.com/2009/08/iphone-tutorial-memory-management.html
Synthesizing a property doesn't actually instantiate an object for you. You'll still need your alloc and init methods.
In the code above, if you're wanting to use the property, you should be using self.calendarView rather than just calendarView. (Doing the latter is bypassing the property and using the instance variable directly, which is usually not what you want, with the possible exception of in your dealloc method.)
The one final change you should make: given that your property is marked retain, it'll handle keeping your object around itself. Therefore you should autorelease the object you're putting into it. Try this:
self.calendarView = [[[UIView alloc] initWithFrame:calendarFrame] autorelease];
You actually are not doing anythin wrong in your first example other than a possible memory leak. You just have the wrong thinking that your calendarView is already retained because that was how you defined your property, which is not true. Defining your property as retain only means that when you call self.calendarView = someotherview, someotherview will be retained, the old value in calendarView will be released and calendarView will then be set to someotherview. Using calendarView without self will not provide you with any memory management rules like the property and that is why your first example is ok. You may want your code to look more like this.
CGRect calendarFrame = CGRectMake(170, 8, 200, 50);
self.calendarView = [[[UIView alloc] initWithFrame:calendarFrame] autorelease];
Yes,
You either need to alloc OR get it from others sources (release it in dealloc because you are retaining it ) before using UIView.
Use below
CGRect calendarFrame = CGRectMake(170, 8, 200, 50);
self.calendarView = [[[UIView alloc] initWithFrame:calendarFrame] autorelease];
Read apple documentation for memory management..
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
I therefore declared it in the #interface section
right, that just reserves space and a label to access an ivar with the type+name
and then #property (nonatomic, retain) UIView *calendarView.
right, that declares the accessors (setter+getter), and operation if synthesized
In the #implementation I have #synthesise calendarView.
that defines (implements) the accessors declared by the property declaration.
Now I would like to define the frame of this view and coded the following:
...
But I am now wondering if I am doing something seriously wrong here, as calendarView has already been retained and synthesised. Surely UIView alloc is superfluous or even bound to make the app crash, as I am running into memory problems, right?
for one, your memory management is off:
CGRect calendarFrame = CGRectMake(170, 8, 200, 50);
UIView * view = [[UIView alloc] initWithFrame:calendarFrame];
self.calendarView = view; // use the setter, unless in init... or dealloc
[view release], view = 0;
two:
you will not usually have much use in creating a UIView. typically, you will create a subclass of it.
three (to get to your question):
there's nothing wrong with that. the variable will be nil until it's been set. the field is not default initialized for the declared type -- well, it is, but the type is in fact a pointer, so the result is that it is initialized to nil. you can either create a view or pass it in from someplace else. the view will be nil/NULL/0 until that point.
So I though I should code this instead of the two previous lines, but it only had the effect that the calendarView is not shown at all:
[calendarView setFrame:CGRectMake(170, 8, 200, 50)];
So my question is if I really need to alloc the view before I can use it? Or is there yet another solution?
stepping back to point #2 in more detail: you'll want to create a subclass in most cases. UIView/NSView does no drawing by default, but it can be used as a view container. therefore, you may want to start with some existing subclasses to get familiar with the system-supplied views.
once you have a handle on that, try implementing your own subclasses and overriding drawRect:.
many beginners like using Interface Builder (now integrated into Xc4) -- a WYSIWYG view editor.

Resources