I am currently fixing memory leaks in our SDK. I am getting memory leaks mostly in strong references and class methods. Due to my lack of understanding in memory management, I find it hard to fix those leaks. I already read a lot about memory management but still I was not able to determine where the problems are located. So, I figured that I should know first how does balancing retain/release works just by looking at the code. As a result I switched off ARC did a little test.
This is my source code:
ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, assign) IBOutlet UILabel *lLineOne;
#property (nonatomic, assign) IBOutlet UILabel *lLineTwo;
#end
ViewController.m
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *test = [[NSString alloc] initWithFormat:#"%# %d", #"Test", 1];
_lLineOne.text = test;
_lLineOne.text = nil;
}
#end
I pretty much know that this will definitely cause a leak, because I didn't get to release test.
Then I use the instruments(leaks) to check the retain/release list. And here is what I got from the history of that string
I know that event#0 which is the malloc will increment the retain count to 1, then followed by setting that NSString to the property of UILabel will also increment the retain count by 1, so that gives us a 2. I guess the CFRetain is something that happens at a lower level in the code of NSString allocInitWithFormat but I am not sure why it contributes to another +1. Followed by that, what is that Retain in event#3? It seems that it is caused by UILabel, but I really don't have any idea about what it really is.
First the standard warning: trying to track the retain count is not recommended, for the simple reason it is not in general possible for you to know what it should be.
You are seeking an answer you don't need. Most of the time all you really need to know under manual memory is if you create or retain you are responsible to arrange the release.
In your example you create the string, so you must arrange it's release after you no longer require it. UILabel may copy or retain at its choice, you don't need to know - you have to trust it follows the rules.
The "most of the time" above is largely to do with delegates. It is common for objects passed as delegates not to be retained by the callee, leaving the caller to keep the object around as long as the callee needs it.
HTH
Related
I have an object called PanelView in Cocoa Touch which has a UINavigationController instance variable. At some point in my app, when the UINavigationController is presented as modal view, I send in a PanelView object as a parameter to the rootViewController of the UINavigationController where it is stored as an instance variable. I declare it how I would normally in the .h file:
PanelView *_panelView;
#property(nonatomic, strong) PanelView *panelView;
And in the .m file:
#synthesize panelView = _panelView;
So far I've had no side effects, but I'm wondering if I'm declaring this correctly as it might be a circular reference. What is the correct way to declare this variable?
Objective-C has evolved to be pretty forgiving on syntax, especially on iOS. You don't actually need to declare the variable that's acting as the backing store for your #property -- it will be synthesized for you.
It used to be the case that if you declared both a getter and a setter, then you would need to insert the #synthesize in your #implementation block, as you have it above. I just tried it out in Xcode 7.1 to double-check, and the compiler didn't complain for either a Cocoa Touch project or a command-line app project.
It seems from your follow-up question that you need to sort out the ownership of your data. Circular references in and of themselves usually aren't the problem. Keeping strong circular references is. That's how you get memory cycles that lead to leaks.
I would advise that you change the UINavigationView's reference to be a weak reference, since it the PanelView is guaranteed to be in existence as long as the UINavigationView is in existence.
I have the problem in an iOS app that after some time, an object isn't dealloc'ed as it should be. I suspect that this is because there is still a reference to it. I'm using ARC.
I want to find out where that reference is created. I will then be able to tell where it should be NULLed or if it should be made a weak reference.
What I imagine as a possible solution:
If I could set a breakpoint for every place where the reference count, aka retain count, is modified, then I will be quick to find the problem. I just don't know how to set such a breakpoint. Maybe at pre-ARC times, this could have been done by setting breakpoints inside retain and release, but I have no idea how to do this with ARC.
Highly simplified example code:
I've done this in one of my classes and I know where:
// ShouldBeDeallocated.m
- (void) dealloc {
NSLog(#"%s", __FUNCTION__); // this never shows up in output
}
And I suspect that I wrote code like this some time ago, but I can't find where that was:
// UnknownSuspect.m
#interface UnknownSuspect ()
#property (strong, readwrite) id referenceWhichIsNeverNeeded;
#end
- (void) someMethod:(ShouldBeDeallocated*)ref {
self.referenceWhichIsNeverNeeded = ref;
// The object pointed to by referenceWhichIsNeverNeeded will
// not be dealloc'ed unless the property gets overwritten.
}
You can use Instruments to profile memory allocations. Then you can see where in your code it is allocating it and changing the retain count. Instruments can also help track down where you have memory leaks in your code, which is still possible even with ARC.
I'm currently working on fixing some iOS 7 display issues on a legacy app which was not built with ARC, and have run into some inconsistent behaviour with the dealloc method between iOS versions 6 & 7. I can't find any other mention of these changes in any documentation or community discussions, so I wonder if anyone here could shed some light on what's happening here?
My previous code, (which works in iOS6, looks like this):
#interface MyViewController()
#property (retain) AdHandler *adHandler;
#end
#implementation MyViewController
#synthesize adHandler = _adHandler;
- (id) initWithAdHandler:(AdHandler*)anAdHandler
{
self = [super init];
_adHandler = [anAdHandler retain];
return self;
}
- (void)dealloc
{
[super dealloc];
[_adHandler release];
_adHandler = nil;
}
...
#end
When stepping through the code in iOS 6, I've found that after the dealloc statement, [_adHandler retainCount] is still positive, and the object is still available.
In iOS 7 however, after the dealloc statement, retainCount has somehow hit zero, and the _adHandler object has been dealloc'd, and therefore my call to release causes an EXC_BAD_ACCESS.
I can fix this simply by moving my [adHandler release] call to before the dealloc call, but my question is why is this happening? Why is dealloc releasing objects that it has no responsibility for? Is there any documentation anywhere on why dealloc behaviour has changed in this way?
After [super dealloc] the instance is garbage and whatever happens is rather random and non-deterministic. As #bneely wrote, [super dealloc] must be last.
Best practice: convert to ARC.
As for retainCount, there are no guarantees what it's value may be, don't use it, it just caused confusion. In your case you destroyed the class instance by calling [super dealloc] and then expect the instance to behave as if it still exists. It can't, it has been destroyed and is now just some non-deterministic bits in memory.
You should never use retain count.
The values it returns cannot be interpreted by you in any reasonable way. Thus it's entirely expected to see different results on different iOS versions, devices, with different code bitness, etc.
On a side note, do you consider switching to ARC? The code would greatly simplify. Also note that if you're implementing a subclass of UIViewController, you shouldn't initialize it with init. Rather just declare the property and ARC will take care of its setters and getters:
#interface MyViewController : UIViewController
#property AdHandler *adHandler;
#end
// somewhere else
MyViewController * mvc = ... from nib or in some other way ...
mvc.adHandler = myAdHandler;
Now you're guaranteed no bad accesses.
I am little confused about specifying strong, copy, or assign and not specifying them. We don't use NIB files. My colleague always use following - he says iOS detects it and use it automatically strong, weak etc.
#interface viewController : UIViewController
#property (nonatomic) UIImageView *imageView1;
#property (nonatomic) NSUInteger num;
#property (nonatomic) NSArray *array;
#end
I prefer following way doing it.
#interface viewController : UIViewController
#property (nonatomic, strong) UIImageView *imageView1;
#property (nonatomic, assign) NSUInteger num;
#property (nonatomic, copy) NSArray *array;
#end
Which one is better programming style? First option always have strong type as it defaults but I always specifies them explicitly.
As pointed out in earlier answers, in Objective C properties are by default,
atomic, strong/retain, readwrite --> For pointer types
atomic, assign, readwrite --> For primitive types
Property types weak, copy need to be explicitly specified by the programmer and in no way gets automatically decided.
What does each mean,
strong/retain referenced objects are kept alive in the memory until specified.
weak referenced objects will be destroyed when there is no strong reference. Typically used to reference delegate objects.
copy will create a shallow copy of the object getting assigned to the property.
assign/usafe_unretained(ARC) assigns the value. If used in case of pointer types this is an unsafe unretained assignment of the pointer. In ARC, typically use weak for pointer types because it will make the ivar=nil once weak referenced object is destroyed. assign in this case will lead to dangling pointer.
Personally, I prefer specifying property types even if it strong by default. This adds readability, which comes handy specially when profiling application for memory leaks or debugging crashes.
You can read more about properties here.
Hope that helps.
With ARC, strong is the default so the two are technically the same. From the Transitioning to ARC Release Notes:
__strong is the default. An object remains “alive” as long as there is a strong pointer to it.
Note that ARC will NOT automatically detect when something needs to be weak, however.
I tend to be explicit, like your second example, but I think it's mostly a matter of style/habit.
The default for a property is strongand for a variable is __strong too. In your current example the recommended property would actually be weak, but strong is acceptable too.
For properties of non-primitives you should no longer use assign, but rather unsafe_unretained. Practically they're the same, but the latter lets you know that you're using an object unsafely.
The copy property means that an object is copied (using the copy method) and not retained. It's recommended for classes such as NSString and NSArray, which have mutable forms. This is because you don't want to retain what you think is an immutable string but is actually being changed elsewhere.
The assign property declaration should be used only for primitive types and structs, e.g. int and CGSize.
I sounds like a stupid question, but it seems I cannot release an adMob GADBannerView.
Admob documentation says don't call "release" when using ARC. Needless to say you cannot call release because it's not allowed and generates an error.
I tried this:
#property (nonatomic, strong) GADBannerView *adMobView;
…
[adMobView removeFromSuperview];
adMobView.delegate = nil;
adMobView = nil;
But nothing happens. It becomes nil but still stays on the screen. It supposed to be a subclassed UIView. At the best I can hide it but it still received ads and obviously stays in the memory.
Any Ideas?
Try weak reference
#property (nonatomic, weak) GADBannerView *adMobView;
Weak
weak is similar to strong except that it won't increase the reference count by 1. It does not become an owner of that object but just holds a reference to it. If the object's reference count drops to 0, even though you may still be pointing to it here, it will be deallocated from memory.
Refer more here