We are subclassing UITabBarController as well as UITabBarControllerDelegate to handle certain events concerning tab switches.
Now in our custom tab bar controller we have:
- (id)initCustomTabBarController {
self = [super init];
if(self) {
[self setDelegate:[[CustomTabBarControllerDelegate alloc] init]];
// ...
}
return self;
}
Since we transitioned the project to ARC, the delegate is released to early which causes a tab switch run into a deallocated instance.
The property is defined as assign in UITabBarController.h - which I obviously have no influence on.
What can I do to make the delegate object "live" longer than for the init method?
The way you have done it, it is expected that the delegate will not outlive the object, because it is weak. Remember, you created the object, it's up to you to hold on to it.
However - the pattern that you are using is incorrect.
The point of a delegate, is that it provides method implementations to a class that a class can't add for itself, because it doesn't have enough information. For example, a table view delegate. A table view, in order to be generic, cannot know how many rows or sections to display, so it asks it's delegate to supply this information.
In your case, you have an object that is creating it's own delegate. In which case, why bother having a delegate at all? Just implement the methods in the class.
Yes this would be normal under ARC, since no reference to it is made (aka strong propteries) it should be released at the end of cycle.
Just make a property in the class where you assign the CustomTabBarControllerDelegate take make it strong. Then assign this property to the delegate.
In non ARC the way you have set it up you could have create a memory leak.
Related
Quick question... Well I understand that all properties start out as nil in Objective-C and that sending a message to nil does nothing, therefore you must initialize using [[Class alloc] init]; before sending a message to a newly created property. However, what about if I'm not sending messages to this property or if I set the property using self.property = something? Do I need to alloc init in these cases as well? Also, do UI properties start out as nil as well, such as a UILabel property that you drag out from your storyboard? Do these need alloc init?
Thanks to all who answer
Stunner did a good job of explaining not needing to alloc init objects that have already been created.
But if it is an object that doesn't exist, where are you going to create it? A very common pattern, which I mention because you mentioned it in your post, is lazy instantiation.
So you want an NSMutableArray property. You could alloc init it in some method before you use it, but then you have to worry about "does that method get called before I need my array?" or "am I going to call it again accidentally and re-initialize it."
So a failsafe place to do it is in the property's getter. It gets called every time you access the property.
.h
#property (nonatomic, strong) NSMutableArray* myArray;
.m
#synthesize myArray = _myArray;
- (NSMutableArray*)myArray
{
if (!_myArray) {
_myArray = [[NSMutableArray alloc] initWithCapacity:2];
}
return _myArray;
}
Every time you access that property, it says, "Does myArray exist? If not, create it. If it does, just return what I have."
Plus an added benefit with this design pattern is you aren't creating resources until you need them, versus creating them all at once, say, when your view controller loads or your app launches, which, depending on the requirements, could take a couple seconds.
The reality is when you do self.myProperty = [[Class alloc] init], you're not initializing your property. Rather, you're initializing an object that you tell your property (which is in fact a pointer) to point to. So if you already have an object that's allocated and initialized, you don't have to alloc/init again and you can do self.myProperty = object;
UI Properties do no start as nil, this is because when you add elements in the interface builder, the view owns the elements that you add and these objects are initialized automatically for you. This means if you're creating IBOutlets and connecting them to some properties, you don't have to alloc/init.
I hope this was helpful.
I don't have experience with Storyboards but I know that when you create objects via a xib file all objects are properly instantiated when you tell a view controller to use a xib file. So you need not worry about alloc/initing those objects in code.
Regarding using self.property = <something>, it depends on what something is. If something is any sort of existing object you need not do the alloc init on that object as the self.property = ... syntax calls the property's setter method which will retain, copy, assign, etc. the new value to the property appropriately.
Now any sort of existing object can be an alloc/init'ed object, or an autoreleased object obtained from a convenience method (NSString's stringWithFormat: for example).
As Kaan Dedeoglu pointed out, the self.property = ... syntax points (and retains) the ivar to the object in memory, and it is up to you to initialize that object if it isn't already instantiated.
No you do not need to [[Class alloc]init the properties in your init method.
However, I would encourage you to explicitly set them to Nil in your init method for clarity.
I'm fairly new to Objective-C and am trying to get to grips with the complex world or memory management.
I'm building an iPhone app and have a global singleton object that is used by various view controllers.
I'm trying to shorten the code I have to write each time I access this global object and want to store a reference to this object as an instance variable in the view controllers.
My view controller .h file:
#interface MyViewController : UIViewController {
MyGlobalObjectType *_myobject;
}
#end
This is my init method of my view controller .m file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder])) {
_myobject = [[Global sharedGlobal] myobject];
}
return self;
}
My question is when I store a reference (pointer) to another variable do I have to call retain on it when initialising that instance variable?
In general, no. Memory management with a singleton object can be a tricky thing to get right, but usually, you want the singleton to be initialized once throughout the course of the application and not deallocated until the application quits.
In this case, when your object (inside the singleton) gets initialized, it has a reference count of +1. If you were to call retain on that object, its reference count would go up to +2. This is a perfectly okay thing to do as long as you remember to call release on it when your view controller is done with it, bringing its count back down to +1. That said, you usually don't need to do this.
In other words, remember that it is the object that has the reference count. Pointers to the object do not, so there's no need to send a retain message in order to keep this object alive.
Why would you want to hold a reference to your singleton? That defeats the purpose of creating one in the first place. Just get a reference to singleton at the moment you need it.
I've done all sorts of research today on best practices with regards to declaring IBOutlets and instance variables, managing them, using the correct accessors and properly releasing them. I'm pretty much there, but I've got some niche questions that I hope somebody will be able to advise the best practice on. I'll format them as code and comment the questions so as to make it easier to understand. I've excluded some obvious parts that I didn't think were relevant and can be safely assumed to work (like pre-processor stuff, #end, required implementation methods etc).
MyViewController.h
#class OtherViewController;
#interface MyViewController : UIViewController {
NSString *_myString;
BOOL _myBOOL;
}
// The first two properties aren't declared in the interface
// above as per best practices when compiling with LLVM 2.0
#property (nonatomic, retain) OtherViewController *otherViewController;
#property (nonatomic, retain) UIButton *myButton;
#property (nonatomic, copy) NSString *myString;
#property (readwrite) BOOL myBOOL;
MyViewController.m
#implementation MyViewController
// Synthesizing IBOutlets on iOS will cause them to be
// retained when they are created by the nib
#synthesize otherViewController;
#synthesize myButton;
// Assign instance variables so as to force compiler
// warnings when not using self.variable
#synthesize myString = _myString;
#synthesize myBOOL = _myBOOL;
- (void)viewDidLoad {
// QUESTIONS:
// 1. Ignoring convenience methods, can you still alloc and init in dot notation
// even when it's being properly synthesized?
self.myString = [[NSString alloc] initWithString:#"myString"];
self.myString = existingNSStringObject;
// 2. Should you always call methods for IBOutlets and instance variables using dot notation?
// Is there any difference seeing as these aren't directly invoking setters/getters?
[self.myButton setText:self.myString];
[myButton setText:self.myString];
[self.otherViewController.view addSubview:mySubview];
[otherViewController.view addSubview:mySubview];
[self.myButton setAlpha:0.1f];
[myButton setAlpha:0.1f];
self.myButton.alpha = 0.1f;
myButton.alpha = 0.1f;
// 3. How fussy are scalar variables in terms of getters and setters,
// given that there is a #synthesize declaration for them?
self.myBOOL = YES;
myBOOL = NO;
if(self.myBOOL) { ... }
if(myBOOL) { ... }
// 4. On instantiation of new view controllers from NIBs, should you use
// dot notation? (I haven't been doing this previously).
otherViewController = [[OtherViewController alloc] initWithNibName:#"OtherView" bundle:nil];
self.otherViewController = [[OtherViewController alloc] ... ]
}
- (void)viewDidUnload {
// 5. Best practice states that you nil-value retained IBOutlets in viewDidUnload
// Should you also nil-value the other instance variables in here?
self.otherViewController = nil;
self.myButton = nil;
self.myString = nil;
}
- (void)dealloc {
[otherViewController release];
[myButton release];
[_myString release];
}
I always declare and explicitly set a property's underlying instance variable. It's a little more work up front, but in my mind it's worth it to explicitly differentiate variables and properties and see at a glance what instance variables a class has. I also prefix instance variable names, so the compiler complains if I accidentally type property instead of object.property.
Calling alloc / init creates an object with a retain count of 1. Your synthesized property will also retain the object, causing a memory leak when it's released (unless you release your property right after, but that's bad form). Better to alloc / and release the object on a separate line.
Dot notation is effectively the same as calling [self setObject:obj]. Not using dot notation accesses the underlying instance variable directly. In init and dealloc, always access the instance variable directly as the accessor methods can include extra operations (such as key value observing notifications) that are not valid when the object is being created or destroyed. All other times use the synthesized accessor methods. Even if you're not doing anything special now, you might later override these methods later to change what happens when the variable is set.
Scalars work the same way, only you don't have to worry so much about memory.
One accesses the synthesized accessor methods, the other accesses the instance variable directly. See questions one and two again, and be careful about memory leaks!
The view controller may be pushed onto the screen again, in which case your viewDidLoad method will be called a second time. If you're setting initial values in viewDidLoad, go ahead and set your properties to nil here. This makes sense for properties that use a lot of memory and aren't going to affect the state of the view. On the other hand if you want the property to persist until you're sure it's no longer needed, create it in your init method and don't release it until dealloc.
1) You've slightly misunderstood #synthesize. #synthesize does nothing with the object. It only tells the compiler to generate the getter and setter methods according to the options used in your #property declaration
// Synthesizing IBOutlets on iOS will
cause them to be
// retained when they
are created by the nib
The outlets aren't retained (outlets are just notices to interface builder and don't affect the code), the objects are retained when the setter generated by #synthesize is used. When the nib is loaded, the loading system calls your generated setter.
2) Deciding whether to use accessors in objective C is no different from deciding to use accessors in any other object oriented language. It is a choice of style, need and robustness. That the accessor is serving as an IBOutlet makes no difference.
But in objective C I would suggest you should NOT use accessors in two places: dealloc and within the var's accessor method itself.
And if you ARE using the accessors in init then you need to be careful about your retain counts.
self.myString = [[NSString alloc] initWithString:#"myString"];
This line leaks memory. Using your copy accessor retains the object, so you should release it here after creating it.
3) Not sure what you mean by fussy. Possibly see answer to 2)
4) See 2) and be careful about memory management. If you call alloc/init you are now responsible for releasing the object - this is entirely independent of the retains/releases used by accessors and dealloc.
5) No, you should not nil other instance variables in viewDidUnload. Your controller is expected to maintain its state even if the view goes away. viewDidUnload is only for cleaning up potentially memory-heavy view objects when the controller's view is not currently on screen.
Consider a navigation controller. View controller 1 is on the stack and then view controller 2 is pushed and is now visible. If memory conditions get low, the system could attempt to unload view controller 1's view and will then call viewDidUnload.
Then popping view controller 2 will not create the view controller 1 object again, but it WILL load view controller 1's view and call viewDidLoad.
Re comments
2) That's exactly right - you can use a convenience constructor or release immediately after your alloc/init and assignment, or release before the block exits, or autorelease. Which you choose is mostly a matter of style (though some would argue against autorelease - but not me!)
3) There are accessors for scalars - you have created some in your code
#property (readwrite) BOOL myBOOL;
This creates methods myBOOL and setMyBOOL on your class.
Remember that there is nothing special about dot notation. It is only a convenience and when the code is compiled myObject.property is exactly equivalent to [myObject property] and myObject.property = x is exactly equivalent to [myObject setProperty:x]. Using dot notation is purely a style choice.
Dot notation and brackets notation are pretty much the same.
By self.myVariable you are accessing the getter of the property of the instance variable myVariable and by myVariable you are accessing the local variable. They're not the same thing.
You can customize the setters and the getters by overriding the methods and specific some certain conditions for them.
See first answer ( brackets are preferred - better understanding of the code )
Better make a separate method.
Like:
- (void) releaseOutlets {
self.firstOutlet = nil;
self.mySecondOutlet = nil;
……………………
self.myLastOutlet = nil;
}
and then call this method both in viewDidUnload and in dealloc methods.
Hope it helps !
I'm currently learning objective-c and I'm currently training with NSTableView.
Here is my problem :
I have linked my tableview to my controller through Interface Builder so that it has a datasource, I have implemented NSTableViewDataSource protocol in my controller and I have implemented both -(NSInteger) numberOfRowsInTableView: and -(id) tableView:objectValueForTableColumn:row: methods.
I have created a raw business class ("person") and I succeeded to display its content into my NSTableView.
But then, I put some NSLog in my dealloc methods to see whether the memory was freed or not and it seems that my array as well as my "person" instances are never released.
here is my dealloc code in the controller:
-(void)dealloc
{
NSLog(#"the array is about to be deleted. current retain : %d",[personnes retainCount]);
[personnes release];
[super dealloc];
}
and in my "person" class
-(void) dealloc
{
NSLog(#"%# is about to be deleted. current retain : %d",[self prenom],[self retainCount]);
[self->nom release];
[self->prenom release];
[super dealloc];
}
When these deallocs are supposed to be called in the application lifecycle? Because I expected them to be called at the window closure, but it didn't.
In the hope of beeing clear enough,
Thanks :)
KiTe.
I’m assuming you’re never releasing the window controller object that owns the (only) window. As such, the window controller and every top level object in the nib file are retained throughout the application lifecycle, including the window (and its views).
Since the window controller exists throughout the application lifecycle, it isn’t released, hence its -dealloc method is never called. And, since the controller -dealloc method is never called, its personnes array isn’t released.
The personnes array owns its elements. Since the array isn’t released, neither are its elements, hence the -dealloc method of the corresponding class/instances is never called.
Don't ever use retainCount. The results are misleading at best. If you practice proper memory management practices, you'll be fine. Have you had any memory issues/crashes?
I have a couple of questions relating to UIViewController:
1) When are each of the methods called for UIViewController? Specifically, the difference between viewDidLoad, viewDidUnload, and dealloc.
2) What's the difference, in general, when setting a pointer equal to nil and releasing the pointer? I know that in viewDidUnload you're supposed to set it equal to nil but in dealloc call release.
UPDATE: Sorry, just realized the question is misleading. Instead of dealloc, I meant -- when is initWithNibName:bundle: and release called? Just once by IB, right?
Setting a pointer to nil doesn't release the memory that it points to.
When you do something like
self.pointer = nil;
it's usually a case that the property has a retain attribute. When this is the case, setting the property to nil will indirectly cause a
[pointer release];
pointer = nil;
In the case of the view controller methods, viewDidLoad is called when your view is loaded, either from a nib, or programatically. More specifically, it's called just after -loadView is called. You shouldn't need to call loadView manually, the system will do it. The viewDidUnload method is called in the event of a memory warning and your view controller's view is not onscreen. Subsequently, loadView and viewDidLoad will get called again on demand.
The dealloc method, as normal, is called when your object's retain count reaches 0.
pointer = nil; // just clears the variable in which you store the pointer, but does not free memory.
[pointer release]; // just frees the object (memory), but does not clear the variable used to point to it.
self.pointer = nil; // sets the variable to nil. Also releases the object ONLY if pointer is a #property(retain) ivar.
One easy way to see when various methods are called is to do this in your UIViewController:
- (void)viewDidLoad
{
NSLog(#"MyViewController::viewDidLoad");
[super viewDidLoad];
// the rest of your viewDidLoad code, here.
}
// Etc., for the other methods of interest.
NOTE: much can be gleaned from overriding retain & release to log and then following along in the debugger.