I haven't seemed to run into a problem yet, but I'm trying to make sure I'm using some best practices.
Say I have a UITableViewController with a data source of an NSArray of MyObject objects. So in my UITableViewController I declare my data source like:
#property (strong, nonatomic) NSArray *dataSource;
Then after I touch a cell I want to push a new view that shows a detail view of something, using that cell's MyObject. So in the new UIViewController I have this:
#property (strong, nonatomic) MyObject *myObject;
Now in the UITableViewController when a cell is touched:
MyObject *myObject = [[self dataSource] objectForIndex:[indexPath row]];
NewView *view = [[NewView alloc] initWithMyObject:myObject];
// ... push onto nav controller, etc
Basically what I'm afraid of is my array is declared with strong, MyObject in the detailed view is declared with strong, and who knows maybe there is another view with the same MyObject declared with strong.
Bottom line: is this the proper way to pass an object in between views? I haven't really used a weak reference yet in my apps and I feel like that isn't right. Any help or links to help would be amazing.
I think that what you need to understand is how arc works.
Basically whatever has a strong pointer pointing to it will be retained.
This works by adding a reference counter in the object so when you do this:
#property (strong, nonatomic) MyObject *myObject;
you create a STRONG pointer for myObject, (not the object).
but when you do this
MyObject *myObject = [[self dataSource] objectForIndex:[indexPath row]];
you make this pointer increase the reference counting on whatever you have in the specified index from that data source.
The important part is that as long as the pointer keeps pointing to this object it will be kept alive.
About your concern with the views.
Views created in the interface builder have their elements declared internally with strong pointers. This is when you want to use a weak reference. When you add your own IBOutlet to an element in the view it is good practice to make it weak. If you think about the reason logically, it basically means that you dont care about this interface builder element since you only want it to survive until the viewcontroller is deallocated.
When you usually encounter retain cycles is when an object has a child object, and this child object has a STRONG reference to its parent.
this is:
Object A creates object B with a strong pointer
Object B points to object A with a strong pointer
A will keep B alive and B will keep A alive.
This page will explain to you some basic stuff about how to avoid this kind of stuff:
http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html
Also about passing objects between views, it is very very simple.
First get a pointer from View 1 to View 2 (can be strong or weak depending on who should be keeping view 2 alive, if its from the IB Builder it should be weak if its programatically it should be strong)
Second, make a property in view 2 (#property (strong, nonatomic) MyObject *myObject;)
now it is as simple as:
Self.view1Pointer.myObject = self.myOtherObject;
Understand here how both views are strongly pointing to this object so the object will be kept alive as long as 1 of the views hasnt been deallocated.
You wont create a retain cycle, you simply have the reference counting from that object set to 2.
Note: When a view is deallocated, all of its pointers are set to nil so any object being pointed by them will decrease in its reference count. IF it reaches 0 it is deallocated. (in the previous case myobject will be 1 because another view is still pointing to it).
The only scenario where you will create a retain cycle is if you manage to make myObject point strongly to View2 as well. So now they are keeping each other alive. (but as explained before you can make myObject point to view2 weakly which wont create a retain cycle).
You can learn more about arc here:
http://www.raywenderlich.com/5677/beginning-arc-in-ios-5-part-1
It's not a retain cycle. It's a fine way to do things. The instance of MyObject doesn't strongly reference any of its owners, so when the last of those owners eventually gets released, so too will the object.
Related
I've been reviewing the Apple docs and sample code to try to determine the best way to manage memory for IBOutlets. I'm a little confused, to say the least.
The CurrentAddress sample code declares IBOutlets as properties:
#interface MapViewController : UIViewController <MKMapViewDelegate, MKReverseGeocoderDelegate>
{
MKMapView *mapView;
UIBarButtonItem *getAddressButton;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *getAddressButton;
Great. And these are released in dealloc:
- (void)dealloc
{
[mapView release];
[getAddressButton release];
[super dealloc];
}
Now shouldn't these properties be set to assign? Because when set to retain, the IBOutlet's retain count will be increased twice: once when the nib is loaded and another time when the property is set? And wouldn't it be better to set these properties to nil instead of releasing in dealloc?
Apple docs says we should retain properties for iOS.
Retained outlets should be released and nil'ed in both dealloc and viewDidUnload.
On the Mac, every outlet which is not retained by a superview is automatically retained when loading the nib. That's not the case with iOS. That's why it's theoretically valid to only retain outlets other than views in the view hierarchy.
There's a very helpful post by Jeff LaMarche regarding this topic: Outlets, Cocoa vs. Cocoa Touch.
Once the nib loader finishes loading everything and connecting all the IBOutlets, it autoreleases all the objects it loaded. If your IBOutlet property was declared as assign, then the object it points to would be deleted next time the autorelease pool emptied.
You can set the properties to nil in dealloc instead of directly releasing them, the result is the same. The thing to watch for is, if you've provided your own implementation of the setter, you need to keep in mind that some of the other members of your object may already have been released.
This is different for MacOSX and iOS. In iOS the retain count will be two after the view is loaded and the nib connections are established.
Each of these elements will be retained once by the view and once by your controller. Additional elements in the view will be retained only by the view only.
When your controller releases the two elements, their retain count goes down to one. After that [super dealloc] is called. UIViewController has a [view release] in its dealloc, so the view is released (unless retained elsewhere, or previously released). When the view is deallocated, it releases its sub views, and the elements are finally completely freed.
The reason why [object release] is preferred in dealloc, is that key-value coding (or your own code) might cause additional code to be run when you write [self setObject:nil]. This can potentially cause other objects to interact with your controller when it is in the middle of deallocating itself. Setters should not be used in the init method for the same reason.
There is a second reason for just doing release. By leaving the value and not setting it to nil, we'll notice if code erroneously access that variable on our object later during dealloc. This can help catch bugs that might not be easy to track down otherwise.
I'm assuming you #synthesize these properties. If you didn't, you would need to release yourself manually. You are very correct in your assumption that if you continued to retain when a property is set, you would leak memory.
Let's think.... what did properties used to look like before we had the fancy #synthesize statement?
id _propertyName; // the ivar
- (id) propertyName {
return _propertyName;
}
- (void) setPropertyName:(id)v {
if (_propertyName) {
[_propertyName release]; // release the previously retained property
}
_propertyName = [v retain]; // retain this one so it doesn't fly away on us
}
Now, you don't need to type this stuff, because #synthesize is cool and generates that for you, it will also generate #synchronized blocks if you do not specify something as being nonatomic, which is also pretty rad.
If you specified assign instead of retain, you'd get something like this
id _propertyName; // the ivar
- (id) propertyName {
return _propertyName;
}
- (void) setPropertyName:(id)v {
_propertyName = v;
}
This is about the only thing you CAN do when things are not objects, because they are only values (also sometimes referred to as value types, objects are reference types). Since value types cannot be retained, the other type of block wouldn't make any sense. Go ahead and try to create a retain property with BOOL and watch what LLVM or GCC tell you to go do with what ;)
shouldn't these properties be set to
assign? Because when set to retain,
the IBOutlet's retain count will be
increased twice: once when the nib is
loaded and another time when the
property is set
well, the code you posted is right as it is.
when you use:
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
you are just telling xCode to create a setter method which will create your MKMapView object and retain it everytime you call
yourMapViewController.mapView = someMapView; // from out
// or
self.mapView = someMapView; // from in
after that mapView retain count increase +1
and your MapViewController code need that 'couse now you can point to mapView and manage it...
don't worry for the IB nib file...
when you load a UIViewController with a nib, in your case the class
MapViewController : UIViewController, the IB nib objects will release when you release your MapViewController...
just care of the objectS you retain...
Can any one explain Retain cycle with example code(Objective C) ? and How can we remove retain Cycle ?(with code or diagram). I know about it theoretically but i never come across such kind of program ? I am very curious to see, how retain cycle solved (with code or diagram) ?
Delegation is one example where you have to avoid a retain cycle by using the assign attribute on a delegate property. For example, you have a parent object which creates a child:
self.child = [[[Child alloc] init] autorelease];
So the parent has a retained reference to the child (because the property setter retains it).
Now the parent sets itself as a delegate on the child:
self.child.delegate = self;
Now, if the child retains its delegate property there is a retain cycle. Both contain references to the other and cannot be deallocated.
To avoid this the child declares the delegate property with the assign attribute:
#property (nonatomic, assign) id delegate;
This is safe because a delegate will almost always outlive the delegator. If not, the parent should set the child's delegate to nil before it goes away.
The memory management aspects of Objective-C are very well covered in many places.
Some references:
http://www.otierney.net/objective-c.html#memorymanagement
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
You might consider coming back here if you have specific solutions of which you are unsure, so that we have concrete examples to comment on. You post your code, do not hesitate if you are afraid it might look very stupid or full of mistakes. That is the best way to learn.
Say I have a tableview class that lists 100 Foo objects. It has:
#property (nonatomic, retain) NSMutableArray* fooList;
and I fill it up with Foos like:
self.fooList = [NSMutableArray array];
while (something) {
Foo* foo = [[Foo alloc] init];
[fooList addObject:foo];
[foo release];
}
First question: because the NSMutableArray is marked as retain, that means all the objects inside it are retained too? Am I correctly adding the foo and releasing the local copy after it's been added to the array? Or am I missing a retain call?
Then if the user selects one specific row in the table and I want to display a detail Foo view I call:
FooView* localView = [[FooView alloc] initWithFoo:[self.fooList objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:localView animated:YES];
[localView release];
Now the FooView class has:
#property (nonatomic, retain) Foo* theFoo;
so now BOTH the array is holding on to that Foo as well as the FooView. But that seems okay right? When the user hits the back button dealloc will be called on FooView and [theFoo release] will be called. Then another back button is hit and dealloc is called on the tableview class and [fooList release] is called.
You might argue that the FooView class should have:
#property (nonatomic, assign) Foo* theFoo;
vs. retain. But sometimes the FooView class is called with a Foo that's not also in an array. So I wanted to make sure it was okay to have two objects holding on to the same other object.
To answer your main question, yes you can multiple objects retaining an instance. That is exactly the point of reference-counted memory management. Have a look at the Cocoa Memory Management Programming Guide for more info. Then re-read it. It has all of the answers and will be your best friend.
Basically, sending a -retain message indicates that the sender "owns" the receiver in the sense that the receiver should not be deallocated until all owners have released their ownership. Thus, individual instances don't need to know (nor should they care) whether other owners exist. Retain anything you need to keep around and release it when you're done with it. When all owners have released their ownership, an intsance can be deallocated.
On a side note,
#property (retain,readwrite) NSMutableArray *myArray;
declares that the class declaring this property will retain the NSMutableArray instance. NSArray, NSDictionary, and NSSet (and their mutable subclasses) always retain their contents.
As others say, what you are doing is correct, and the code looks correct to me. I have tens of references to the same object in my code and as long as I have balanced all the retains and releases, everything works fine.
To add a bit more detail... you ask:
because the NSMutableArray is marked as retain, that means all the objects inside it are retained too?
These are two different things. All collection classes (Dictionaries, Arrays, Sets) automatically retain things that you add to them, and release their content objects when the collection object is deallocated. (In case of NSMutableArray, the content object gets released either if you remove it individually from array, or when you deallocate the whole array.)
This has nothing to do with whether the collection object itself is retained or assigned as a property. The only thing to consider there is that if your policy for the collection object property is not correct, it might get released sooner or later than you think and things may get out of balance.
As others say... read the memory management guide, and practice. :) Oh, and read other people's code too from this perspective and try to understand how/why they are doing their memory management.
One other small thing... for every retained property, make sure you have a release call in the object's dealloc method.
Yes, it's ok. That's the entire point of a reference counting memory management system.
I want an array that gets data from one class to be avaliable to me in another class with the same data.
If we declare an array on the applicationDelegate class.
Then declare an object of applicationDelegate in both classes.
And assign the array into appDelegate.array from one class, will i be able get the array across the classes?
I'm with Mike. Leave the App Delegate out of it.
You're in control of when and how your objects are instantiated. If it's important for more than one class to have access to the same data, hand off the data, or a means of getting at your data, as you create instances of the dependent class.
An example, given classes WVParent and WVChild.
WVParent has a property, someArray, which is the array your other objects need:
#interface WVParent : NSObject {
NSArray *someArray
}
#property (nonatomic, retain) NSArray *someArray;
#end
Then you have WVChild, which itself has a property called parentObject:
#class WVParent;
#interface WVChild : NSObject {
WVParent *parentObject;
}
#property (nonatomic, assign) WVParent *parentObject;
#end
Assuming the parent is creating the child instance, you'd allocate it and assign the parent:
WVChild *child = [[WVChild alloc] init];
child.parentObject = self;
Then the child instance can access someArray by:
self.parentObject.someArray
This is just one option. You could also just pass the array itself to the child, assuming the data is static and unlikely to change over the course of the application session.
This way, instead of having a source of data living somewhere in the App Delegate, it lives within a class more appropriately responsible for its creation, maintenance and vending. If you've got a class that pops into existence and can only reach the rest of your app by getting the app delegate, you might want to put a little more thought into your architecture.
More on dependency injection here:
http://en.wikipedia.org/wiki/Dependency_injection
Yes, you could declare a member/property on your applicationDelegate class, although you should try to follow the Single Responsibility Principle and make sure you don't end up stuffing lots of miscellaneous shared stuff in your app delegate (which happens a lot with iPhone code).
Another alternative would be to inject the array into the objects' constructors when you create them.
It's hard to know the best solution in terms of design without knowing what this data is and where it really belongs.
I have some UIImageViews that are nested in UIViews, which group them together. They're owned by an view controller. As soon as the -viewWillDisappear method is called, I want to remove those UIImageViews with their UIViews alltogether, so that the memory gets freed up.
I call -release on the UIViews that contain the UIImageViews as subviews. Nothing else owns the UIImageViews. All I have is an instance variable to the UIView which groups the UIImageViews together.
So when I send -release to it, the retain count should become 0. But that's not a guarantee that I get more free memory, right? So, I also set myUIView = nil. Is that helpful / useful? How do you free up memory safely?
The best thing to do in -viewDidDisappear (not -viewWillDisappear; you're still onscreen at that point) for this case is to call self.view = nil. That will dump the entire view controller's view, which will be automatically reloaded for you next time it's needed.
If you have any IBOutlets on those views, you need to set them to nil, too or they won't release. Assuming you have a setup like this:
#interface MyViewController : UIViewController
{
UIImageView *_imageView;
}
#property (readwrite, retain) IBOutlet UIImageView *imageView;
#implementation MyViewController
#synthesize imageView = _imageView;
Then you need to call self.imageView = nil in your `-viewWillDisappear.
You generally should not call -release on your ivars except in their accessors and in -dealloc. The correct way to release them is to set them to nil using an accessor.
Caling -release when the retainCount is 1 will immediately call free(), which will recover memory. Remember, however, that other things may be retaining the object, including the autorelease pool, so you may not be certain what the retain count is at any given time.
I strongly advise people when they release a variable to immediately set it to nil. This doesn't impact the variable; it just protects you from chasing a pointer that you've released and so may now point to unallocated memory. So in -dealloc, I have lines that look like this:
[_stuff release]; _stuff = nil;
[_otherStuff release]; _otherStuff = nil;