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.
Related
For example:
UIImageView * imageView = [[MyCustomImageView alloc] init];
What is the benefit of doing this? Why not do?:
MyCustomImageView * imageView = [[MyCustomImageView alloc] init];
The benefit is that you can hide implementation details to yourself and to the outside.
If you are going to return this value from a method for example, the outside may not care about what kind of image view it is - as long as it is some kind of it! If it's a private class you are creating, you may not even want to expose that this class exists to the outside.
In other languages with proper interfaces, this is a more well known pattern. This article is a good read.
There are important uses for both. In both cases, I'm assuming that MyCustomImageView inherits from UIImageView...
UIImageView * imageView = [[MyCustomImageView alloc] init];
Assigning a different object to its parent's type is used to extend the standard functionality of UIImageView by overriding public methods and properties and only those. You could, for example, deliver an entirely different image presentation by implementing MyCustomImageView layoutSubviews or drawRect with no other logic in your view controller.
MyCustomImageView * imageView = [[MyCustomImageView alloc] init];
With a declared pointer to MyCustomImageView you can override the public methods and declare new properties and methods that your app requires like invertImage, blinkImage, etc.
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.
I have an UILabel pinter in my UIView (using ARC).
I dynamically create a lot of text and override the the same pointer every time.
I thought, that if I use the same pointer all the time, and override it with the new objects, they still be in my View, but the pointer of them will be deallocated. But as I see, my memory increase all the time, if the text was changed and the drawRect executed. Maybe someone know the better way to do that, or to fix this memory issue.
UPDATE: Code
#interface Bars : UIView{
NSMutableDictionary *dictCopy;
UILabel *pivotLabel;
}
for (a lot of times) {
pivotLabel = [[UILabel alloc] initWithFrame:frame];
pivotLabel.text = pivotText;
pivotLabel.backgroundColor = [UIColor clearColor];
pivotLabel.textColor = self.color;
[self addSubview:pivotLabel];
}
When you add a new label as a subview the parent view retains it. Nilling a pointer is not enough to remove it. To remove a label, do this:
[self.myLabel removeFromSuperview];
self.myLabel = nil;
lets just say I had just one UILabel subview element & one UITextView subview element inside of a given ViewController.m's viewDidLoad method like so:
UILabel *name = [[UILabel alloc] initWithFrame:CGRectMake(20, 140, 280, 40)];
name.text = #"Name: Eric";
UITextView *bio = [[UITextView alloc] initWithFrame:CGRectMake(20, 190, 280, 80)];
bio.text = "Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature fro...";
In a normal fashion I could add them one at a time:
[self.view addSubview:name];
[self.view addSubview:bio];
But if there were several dozen or perhaps hundreds of UI components, adding each of them one at time would add dozens to hundreds of lines of code to my project.
Coming from a ruby background, I believe brevity is very important. Is there any way to pass in my UI elements as an array like so?:
[self.view addSubviews:#[name, bio]];
It might be a little early for me to start patching iOS's libraries at boot time but that would definitely be an acceptable solution to me.
Coming from a ruby/rails template of understanding, I'm wondering, is there something equivalent to Rail's /initializers folder in iOS applications? FYI non/pre-rails folks, the /initializers folder in rails is scanned by rails at the beginning of any rails session, and all code inside its .rb files is executed before anything else in rails is loaded aside from the dependencies libraries which are loaded just before that. Thus the ActiveRecord gem (library) for example could then be reinstantiated in an initializer and be amended to at that point. I know Objective C is immutable in a lot of cases though and don't know if that would be a problem.
Existing Cocoa library methods are of course preferred.
In ruby I'd probably patch the method to UIViewController maybe a little something like this:
class UIViewController
def addSubviews(subviews)
subviews.each {|obj| self.view.addSubview(obj) }
end
end
I'd go for a category on UIView.
File --> New --> File --> Objective-C category. Name it something like EasyAdditionOfSubviews, make it a category on UIView.
UIView+EasyAdditionOfSubviews.h
#import <UIKit/UIKit.h>
#interface UIView (EasyAdditionOfSubviews)
- (void)addSubviews:(NSArray *)views;
#end
UIView+EasyAdditionOfSubviews.m
#import "UIView+EasyAdditionOfSubviews.h"
#implementation UIView (EasyAdditionOfSubviews)
- (void)addSubviews:(NSArray *)views
{
for (UIView *view in views) {
if ([view isKindOfClass:[UIView class]]) {
[self addSubview:view];
}
}
}
#end
This way, you just #import UIView+EasyAdditionOfSubviews.h anywhere you want to be able to add an array of views at a time.
And you're better off making sure that what you're passing to addSubview: is indeed a UIView, otherwise you risk a crash (for example, try passing it an NSArray).
But to address your wider concern:
But if there were several dozen or perhaps hundreds of UI components,
adding each of them one at time would add dozens to hundreds of lines
of code to my project.
If you're building a UI programmatically, you want to be exactly sure what, how, and when subviews are added to a view; it's perfectly fine to be explicit when adding a subview to a view.
As a matter of fact, your workaround to not explicitly adding the subview to its superview is... to explicitly add it to an array, which you then add to the superview. Not good for brevity!
Assuming subViews is an array, you could do:
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[superview addSubview:obj];
}];
As #pranav mentions, an alternative is to use the for..in syntax:
for (UIView* view in subViews) {
[superview addSubview: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.