I have an Manual Reference Count project, where few classes Im converting to ARC by removing retain,release & etc and by setting compiler flag “-fobjc-arc”
Their are 2 ARC(-fobjc-arc) enabled view controller classes, ClassA and ClassB.
I am allocating and initialising objects of ClassB inside ClassA within a for loop to achieve some functionality, Code snippet is as below,
#interface ClassA ()
#property (strong, nonatomic) ClassB *classBObj;
#end
#implementation ClassA
- (void)createClassBView {
for (int count = 0; count <= [dataObject count]; count++) //if count is more than 1 it is not retaining the previous classBObj
{
classBObj = [[ClassB alloc] init]; //ARC is keeping only 1 object reference of this class but I need to retain all the iterated objects
[self.scrollView addSubView:classBObj withFrame:myFrame];//only 1 view is getting added as subview even if control comes here more than once
}
}
#end
The above code works fine for me in non-ARC(MRC) but fails to work properly when ARC is enabled. It is not retaining ClassB objects even if it is strong,
Only 1 object i.e; last iterated ClassB object reference is alive, rest are getting destroyed and it is throwing exception "ClassB reference to an deallocated instance"
I tried by using if(!classBObj){classBObj = [[ClassB alloc] init];} inside loop, that time I'm not getting ClassB reference to an deallocated instance exception but only 1 subview of ClassB is getting added to my scrollview(i.e; last iterated).
Please guide me on this.
Any help is appreciated in advance.
Your code is doing exactly what you are telling it to do. You are setting the very same reference, self.classBObj, to a ClassB instance - over and over, in your loop. Each time through the loop, the existing ClassB instance that was previously assigned self.classBObj needs to "get out of the way" so that a new one can be assigned to self.classBObj. So it is rightly released when it is replaced by the new one - rightly, because there is now no existing reference to it.
The truth is that you were totally mismanaging the memory here before ARC, and adopting ARC has revealed this fact. You're just lucky your code ever worked (or seemed to). If you want to maintain multiple ClassB instances, you need your instance variable to be an array of them, not a single one.
(On the other hand, if classBObj is a UIView and is to be added immediately to the interface as a subview, that is still happening, so it's hard to see what your complaint is. Indeed, the weird part is why you ever needed classBObj to be a property in the first place; why isn't it just a local variable? It's not like you need these references to be retained elsewhere, since you have those references — as subviews of your self.scrollView. But if you need those references for some later purpose, and if you don't want to obtain them by using the fact that they are subviews of your scroll view, then clearly you need an array of them, as I just said.)
Related
In iOS 7 I used to define weak reference to popover controller inside my view controller (which displays in popover). I used this reference to popover controller in order to dismiss popover programmatically. Also, I defined delegate of popover controller in order to track dismissing events (popoverControllerShouldDismissPopover etc).
In iOS 8 it stops working. Investigation shows that weak reference point to nil after some point. The delegate stops working as well (as I understand it's because delegate was defined in popover controller which got destroyed in iOS 8 for some reason after popover displays).
Problem was solved by changed property to be strong reference.
For some popovers (I have bunch of them) I had to add strong reference only for the reason to keep popoverController alive because I need the delegate to work. It's obvious hack. I added property which I don't really need nor use.
Could you please clarify if it's right approach. My concern is that strong reference may lead to memory leaks. Also I don't quite understand why popoverController get destroyed in iOS 8 while popover still on the screen.
This is my view controller with weak property. After changing weak to strong to start working well under iOS 8:
#interface TFDSuggestionViewController : UIViewController
...
#property (weak, nonatomic) UIPopoverController *myPopoverController;
...
#end
This is how I assign value to my property and delegate in prepareForSegue method in calling view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"suggestions"]) {
TFDSuggestionViewController *suggController = ((TFDSuggestionViewController *)[segue destinationViewController]);
suggController.myPopoverController = ((UIStoryboardPopoverSegue *)segue).popoverController;
((UIStoryboardPopoverSegue *)segue).popoverController.delegate = self;
}
}
Thank you for you advice!
In the old days of manual memory management there was something called a reference count. It was essentially the number of times an object was retained (strong reference) by other objects or the App. In the more recent ARC (Automatic reference counting) we no longer need to do [object retain] and [object release]. These actions are handled for us by the compiler.
Now to your situation. A weak reference does not increase the reference count of the object you are referencing. So if you create an object in a scope, assign a weak reference to it, then leave the scope your object's reference count is 0.
-(void)SomeMethod
{
ClassObject *object = [[ClassObject alloc] init];
//object's internal reference count is now 1
self.myPopoverController = object;
//Since myPopoverController is a weak reference, object still has reference count of 1
//Some other code that does things and stuff.
}
//We just closed the scope, so object's reference count is now 0
//ARC is free to release the object to free it's memory, causing any
//weak references to return nil
In the example above it shows a very simple object life cycle. Once you understand the life cycle you can see why a weak reference will do you absolutely no good in this situation.
As to why it worked in iOS7 and not in iOS8 the only answer that I have is that iOS8 is likely much more efficient in garbage collection. If you ran it a million times in iOS7 I'm sure you would find at least one example of the exact same problem happening. It was a flaw in the code that the new OS makes more prevalent.
If you want the object to stay alive you need to have at least one strong reference to it. The only precaution is that when you call dismiss you should nil the strong reference. Then there should be no adverse memory issues to resolve.
Another bit that is very important. The UIPopoverController is not the same object as what is visible on screen. What is visible on screen is the UIPopoverController.view. The view is still retained by the view hierarchy, but the controller needs to be retained by you in order for it not to get released. Once the UIPopoverController is released the view's delegate will be nil since view.delegate is also a weak reference.
Study the object lifecycle. It will help you avoid garbage collection problems that will definitely arise in the future as the OS gets more and more efficient in memory handling.
I have created a class called Item that has two pointers to Item, *containedItem and *container. I declare them as follows:
#property (nonatomic) Item *containedItem
#property (nonatomic) Item *container
I override dealloc within Item as follows:
- (void)dealloc
{
NSLog(#"Destroyed: %#",self);
}
so that I may see which items are being destroyed. I create two items and make it so that one points to the other as its container and the other points to the first as its contained. As the default attribute is being a strong pointer, I would think this would lead to a memory leak. However, when I run my program, it shows that both Items are destroyed. I am wondering how the Items can be destroyed when both have a strong pointer to them (from the other).
All the examples I've come across so far ( including Stanford podcasts ) reference a Model by declaring it as a property of a View Controller and using it from there:
#import "myClass.h" // assume it carries a single NSString property
#interface
#property (nonatomic,strong) myClass *myobject;
#end
#implementation ViewController
-(void)viewDidLoad {
self.myObject = [[myClass alloc] init]
.
.
.
-(void)someMethod{
displayLabel = self.myObject.myString;
seems like that's more self.C-V than M-V-C.
After messin' about on my own this works:
#import "myClass.h"
#implementation ViewController {
MyClass *myObject;
}
-(void)viewDidLoad {
myObject = [[myClass alloc] init]
}
.
.
.
-(void)someMethod{
displayLabel = myObject.myString;
my question is; is there any danger in using second example? or to ask differently, does it give the compiler an easier task of keeping MODEL separate from VIEW and CONTROLLER?
There are a couple of implementation details that are different between your two examples but they are essentially doing the exact same thing.
In both cases you are declaring a backing ivar. This line #property (nonatomic,strong) myClass *myobject; will implicitly #synthesize myObject = _myObject;, which is similar to you manually writing:
#interface ViewController : UIViewController {
MyClass *_myObject;
}
// or
#implementation ViewController {
MyClass *_myObject;
}
The only other difference is that #property (nonatomic,strong) myClass *myobject; will also create the accessor methods for you
- (void)setMyObject:(MyClass *)myObject;
- (MyObject *)myObject;
This is indeed still MVC but controllers that are subclasses of UIViewController always manage at least one view. The M component is your myObject instance. As in most diagrams the Controller sits there managing the communications between the V and M both of which the controller owns
Using singletons or holding the model as a property of the controller is also a question of lifetime. I prefer using singletons for helper classes (getting data from game center or fetching data from core data). Singletons are useful for things you need to access from different places within your app (where lifetime of your singleton is longer than your view controller's lifetime). But for view controller dependent models you can of course use them as properties of the view controller. Let's say you have ten view controllers in your app each displaying completely different content it does absolutely not make sense to keep all data for all possible view controllers in memory (in a singleton) just to have data ready in case the user wants to see any of the view controllers. In this case it's no shame to load your model's data from within your view controller implementation and hold it as a property. This guarantees that data is auto released when the view controller's lifetime ends and avoids conflicts. Holding data in singleton would make sense when you display data loaded from a server that does not need to be refreshed everytime you present your data to reduce the amount of traffic generated by loading the data. Using singletons might be dangerous regarding thread safety e.g. when data is mutated from a background thread while iterating over your datasource object to refresh a table view's content. Singletons can also lead to tigh coupling which should be avoided. Using an instance variable instead of a property is still a good choice if you want to hold a weak reference to an object as it is automatically set to nil if the referenced object gets auto released. A weak property would in this case lead to bad access.
Have an interesting issue where there is a class that is referenced in an XIB layout (subclass of UIScrollView) and is not being de-allocated according to Instruments / Allocations and does not break in it's dealloc routine. Let's call it Sclass1.
There is a using class (let's call it Uclass) that has the XIB file and the outlet.
#property (nonatomic, weak) IBOutlet Sclass1* sclass1;
This is hooked properly to the XIB file layout.
Sclass1 is property allocated when the XIB for Uclass is loaded. Uclass does get deallocated and then recreated from time to time and thus we have another instance of Sclass1, but Sclass1 never goes away and can't find another reference to it.
Drill down in Instruments shows the one Malloc and that is it.
fyi, the class gets started with
[UIClassSwapper initWithCoder:]
If an object doesn't get deallocated under ARC, it means a strong reference to it exists. Since your property is weak the object must be owned strongly by something other than the Uclass object (Otherwise it would get deallocated immediately after the XIB has loaded). In the code you've provided it isn't clear what the actual strong owner of this object is, but I assume it could be one (or more) of the following:
Since the object's class is a UIView subclass, it may be (strongly) referenced by its superview if added as one of subviews. This happens automatically when a XIB file is loaded. If the superview doesn't get deallocated neither will the SClass object. You can remove this ownership by calling removeFromSuperview
A strong ownership cycle (retain-cycle) exists somewhere among ivars of the SClass1 object (i.e. one of the strongly-owned instance variables have a strong reference back to its owner - the SClass1). Beware that any block using self directly also keeps a strong reference. Having a strong reference to the block then often leads to a retain-cycle. Save self to a __weak var and pass that to the block instead unless you have a good reason not to.
A manually created strong reference exists by e.g. adding the object to a container or saving the pointer to a non-__weak variable.
Try finding and removing these strong ownerships. Only after all of them are removed the object can be deallocated.
Since your property is weak and it's still not deallocated, look for strong references to Sclass or it's owner, Uclass. Maybe you are using Uclass(or Sclass) in block directly, without __weak typeof(self) weakSelf dancing and this block creates retain cycle. Also watch for parent-child relations and delegates. Maybe there is delegate which is strong instead of weak or two controllers hold strong references to eachother.
Also, if you want to have more detailed answers, please post more relevant code.
I think your #property should be strong for a class :
#property (nonatomic, strong) IBOutlet Sclass1* sclass1;
Because strong is the equivalent to retain and ARC will manage the release for you.
You will have more information with the Apple Documentation about Transitioning to ARC Release Notes in the section on property attributes.
I recently had the same symptoms - To solve it in my case, my object was acting as delegate for a number of other objects, so had to release the object from all its delegate responsibilities before it would call dealloc
I've been working on a project for several weeks, and recently implemented a singleton object to assist with saving data. After this was implemented, I've been having issues updating labels inside my main view controller.
For example, I'm trying to update the following labels:
#property (nonatomic, retain) IBOutlet UILabel *numDrinksLabel;
#property (nonatomic, retain) IBOutlet UILabel *BACLabel;
with the following code, which is inside a function that gets called on a button press:
BACLabel.text = [NSString stringWithFormat:#"%.2f", user.BAC];
numDrinksLabel.text = [NSString stringWithFormat:#"(%i)", user.numDrinks];
this code block gives me the runtime error:
-[__NSCFString setText:]: unrecognized selector sent to instance 0x1197ef40
However, the same code block called inside viewDidLoad or viewDidAppear is executed with no problems. Initially this suggested to me that there was a problem with my #property declaration, but I get the same error when I change retain to strong, and when I change to weak, the uilabel object is simply null, which is to be expected but nonetheless very frustrating.
So the question is, why would the label objects become dealloced after the viewDidAppear function?
Any suggestions on how to fix this or further test for the root cause would be greatly appreciated!
It seems that your object which contains the iVars numDrinksLabel and BACLabel does no longer exist when you assign something to the text property of the UILabel objects.
Since this happens after you press a button, you have been in the main event loop before. In this loop, any autorelease object will be released if it is not retained by some object.
Thus it seems to me that the object that has your UILabels as iVars is an autorelease object, and it is not retained because you don't use setter methods like self.BACLabel.text = but simply assign methods as BACLabel.text =.
So try replacing your assignments like BACLabel.text = by setters like self.BACLabel.text =, as sixthcent said.
Please check if the superview of these labels is also declared strong