My IOS program is not ARC, code like this:
in the .h file i define five variables:
{
UILabel *label1,*lable2;
UIView *dwView;
NSMutableArray *wordsArray;
}
the code in the .m file like this:
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
viewArray=[[NSMutableArray alloc]init];
}
-(void)QuestionA{
dwView=[[UIView alloc] initWithFrame:CGRectMake(20, 50, 975.0, 620)];
label1 = [[UILabel alloc]initWithFrame:CGRectMake(10.0, 160.0, 950.0, 170.0)];
label2 = [[UILabel alloc]initWithFrame:CGRectMake(30.0, 160.0, 950.0, 170.0)];
[dwView addSubview:label1];
[dwView addSubview:label2];
[self.view addSubview:dwView];
[viewArray addObject:dwView];
[lable1 release];
[lable2 release];
[dwView release];
}
before I turn to another activity I log out the retain count of those variables:
{
[lable1 retainCount] ---> 2
[lable2 retainCount] ---> 2
[dwView retainCount] ---> 3
}
So: I wonder why it like this, and how can I release the retain count
to 0?
Presumably these are the retain counts before you call "release" at the end of your routine.
Calling the traditional "alloc" / "init" starts the newly instantiated object off with a retain count of 1. When you add dwView as a subview to a parent view, that increments the retain count. When you add dwView to the array, that also increments the retain count. Hence 3 there.
Same for the labels, you've added them as subviews, so that increments the retain count by 1 for each (giving you a retain count of 2).
The object will be released when the retain count hits zero (e.g. for your "dwView", that'll be when "self.view" is dealloc'd and when the "viewArray`" gets released).
If use ARC , you may try #autorelease{}
Every time, addview will make a copy of the view.
it has been mentioned many times in apple docs that never go by retain count.
if you are allocating for some memory make sure you call release on that.
its left to system to release that memory chunk and system will do that when retain count reaches zero
below is what apple docs says
Important: There should be no reason to explicitly ask an object what its retain count is (see retainCount). The result is often misleading, as you may be unaware of what framework objects have retained an object in which you are interested. In debugging memory management issues, you should be concerned only with ensuring that your code adheres to the ownership rules.
Related
Maybe this is NOT a duplicate question, as I have searched and tried many solutions about how to release objects under ARC.
The code is simple:
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self recreateView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[self.view addGestureRecognizer:tap];
}
- (void)tapped:(UITapGestureRecognizer *)g
{
[self recreateView];
}
- (void)recreateView
{
#autoreleasepool {
for (UIView *v in self.view.subviews) {
[v removeFromSuperview];
}
MyView *vv = [[MyView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:vv];
}
[self _performHeavyWork];
}
- (void)_performHeavyWork
{
int j = 0;
for (int i = 0 ; i < 100000000; ++i) {
j += random() % 7;
j = j % 18747;
}
}
#end
ViewController simply add a tap gesture recognizer whose action is to remove the old subview before adding a new one. MyView is a subclass of UIView which simply log a message when dealloced.
#implementation MyView
- (void)dealloc
{
NSLog(#"dealloc");
}
#end
The only magic is that the -_performHeavyWork is called every time a new view is created. When you keep on tapping on the screen quickly, the ViewController will be busy creating and discarding views. However, the odd thing is that all the discarded views are not dealloc immediately, but at the time you have stopped tapping for a while.
This is the profile of the process:
As you can see, the memory keep growing if you keep on tapping and so many of MyView instances exist at the same time. And if you comment out [self _performHeavyWork];, everything will be back to normal. So my question is:
Why do this happen?
And how can I solve it?
I think the main problem is that you're performing heavy work on the main thread. If you put the hard stuff on a different thread (or GCD) you'll probably see what you're expecting.
Here's some speculation on what's happening.
iOS responds to changes in the UI exclusively on the main thread. So if you're using the main thread for something else, the taps get queued for later processing.
You tap the screen, the main thread starts processing your heavy work.
You tap the screen some more. iOS can't deal with your request so it queues the event.
Eventually your heavy work completes and returns control to iOS.
iOS takes the queue of events and processes them all in a single run loop, which means the main loops auto release pool is never drained.
But what about the manual auto release pool? Well, all UI related stuff happens on the main loop and on the main thread, so the removeFrmSuperview: won't happen until control returns to the OS. Until that happens, the view hierarchy still holds a reference to your views, hence the memory growth.
You should never rely on dealloc being called when you think the last reference is gone. It is quite possible that references are still there where you don't expect them, but most importantly, dealloc can be called by ARC on a background thread.
First, iOS keeps a reference to a view when you add it to the visible window's view hierarchy. So, when you create a new instance your UIView subclass in the block, it remains in memory beyond the autorelease block. Second, the call to removeFromSuperview: does not actually result in the view being released by ARC until the main thread completes, which means there is still a reference to the view after the autorelease block ends. The work you're performing delays the main thread. This delays removing the final reference to the view.
Also, the autorelease block will not help in the case of removing the view because the view in question was not allocated in the same instance of the autorelease block. IOW, the view being created and added to the view hierarchy is not in the same scope when being removed later in the same block. So, there is no benefit to having the remove call in the autorelease block.
Given the following simple implementation:
#implementation RTUDeallocLogger
-(void)dealloc
{
NSLog(#"deallocated");
}
#end
we run the following code under ARC:
#implementation RTURunner
{
NSArray* arr;
}
-(void)run{
arr = [NSArray
arrayWithObjects:[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
[[RTUDeallocLogger alloc]init],
nil];
NSLog(#"nulling arr");
arr = NULL;
NSLog(#"finished nulling");
}
#end
we get the following log output:
nulling arr
finished nulling
deallocated
deallocated
deallocated
I'd like to perform an action after all the deallocations have finished. Is this possible?
The aim of this question is really to understand a little more about the mechanics of ARC, in particular, at what point ARC triggers these deallocations, and whether or not this can ever happen synchronously when I drop references.
-dealloc is always synchronous, and occurs when the last strong reference is removed. In the case of your code though, +arrayWithObjects: is likely (if compiled at -O0 at least) putting the array in the autorelease pool, so the last strong reference is removed when the pool drains, not when you set the variable to NULL (you should use nil for ObjC objects, btw).
You can likely avoid having the object in the autorelease pool by using alloc/init to create, and you may (implementation detail, bla bla) be able to avoid it by compiling with optimizations turned on. You can also use #autoreleasepool { } to introduce an inner pool and bound the lifetime that way.
If I were an engineer from Apple I'd probably argue that your problem is probably your design. There are almost no reasons you'd want effectively to act by watching dealloc rather than having dealloc itself act.
[a huge edit follows: weak properties don't go through the normal property mechanisms, so they aren't KVO compliant, including for internal implicit KVO as originally proposed]
That said, what you can do is bind the lifetime of two objects together via object associations and use the dealloc of the latter as a call-out on the dealloc of the former.
So, e.g.
#import <objc/runtime.h>
#interface DeallocNotifier;
- (id)initWithObject:(id)object target:(id)target action:(SEL)action;
#end
#implementation DeallocNotifier
- (id)initWithObject:(id)object target:(id)target action:(SEL)action
{
... blah ...
// we'll use a static int even though we'll never access by this key again
// to definitely ensure no potential collisions from lazy patterns
static int anyOldKeyWellNeverUseAgain;
objc_setAssociatedObject(object, &anyOldKeyWellNeverUseAgain, self, OBJC_ASSOCIATION_RETAIN);
... blah ...
}
- (void)dealloc
{
[_target performSelector:_action];
}
#end
-(void)run{
arr = ...
[[DeallocNotifier alloc]
initWithObject:arr target:self action:#selector(arrayDidDealloc)];
/* you may not even need *arr in this case; I'm unclear as
to why you have an instance variable for something you don't
want to keep, so I guess it'll depend on your code */
} // end of run
- (void)arrayDidDealloc
{
NSLog(#"array was deallocated");
}
I've assumed you're able to tie the lifecycle of all the objects you're interested in to that of a single container; otherwise you could associate the notifier to all relevant objects.
The array has definitely gone by the time you get arrayDidDealloc.
at what point ARC triggers these deallocations
ARC inserts allocations/deallocations into your code based on static analysis. You can see where it does this by looking at the assembly of your source -- go to Product -> Generate Output in Xcode.
whether or not this can ever happen synchronously when I drop references
Retain/release/autorelease is always synchronous.
Your code
arr = [NSArray arrayWithObjects:[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
[[RTUDeallocLogger alloc] init],
nil];
will be implicitly placing the objects into an autorelease pool. After the object is allocated, you don't want it retained (because the NSArray will do the retain once it receives the object), but you can't release it immediately, otherwise it will never make it to the NSArray alive. This is the purpose of autorelease - to cover the case where the object would otherwise be in limbo between two owners.
The retain count at alloc time is 1, then it's retained by the autoreleasepool and released by you, so the retain count remains 1. Then, it's retained by the NSArray, so the retain count becomes 2.
Later, the NSArray is released and so the retain count returns to 1, and the objects are finally cleaned up when the autorelease pool gets its chance to run.
You can make the autorelease act faster by nesting another pool - by wrapping your NSArray creation with an #autorelease{} clause.
I am creating view controllers that hold some information to display(image, buttons, gestures, text, etc) and adding them to a scroll.
productsVCs = [[NSMutableArray alloc] init];
for (int i = 1; i <= [self.products count]; i++) {
productsSingle *single = [[productosSingle alloc] init];
single.view.frame = CGRectMake(x, y, 200, 148);
single.delegate = self;
[single setInfo:[self.products objectAtIndex:i-1]];
[scroll addSubview:single.view];
[productsVCs addObject:single];
}
As you can see, I'm doing three important things with these view controllers I'm allocating, I wrote what I think is needed to have ARC release these objects.
1) Set the delegate. -Do I have to set the delegate to nil?
2) Add the view as a subview. -Remove from superview?
3) Add the controller to an array. -Remove from the array?
My question is, when are these view controllers deallocated? Using ARC. Thanks for the help.
The object productsSinglewill be deallocated when your ViewController (that have the scroll view) will be deallocated.
ARC works at compile-time, and so ARC inserts for you the calls to release method on your objects. When you will compile the project, ARC insert the the calls to release method like this:
[single setInfo:[self.products objectAtIndex:i-1]];
[scroll addSubview:single.view];
[single release]; //ARC inserts this line
If you are using ARC you don't have to worry about releasing your objects since ARC will do it for you. Still you can set up strongly held instance variables in your controllers to be released in your viewDidUnload method.
Now, your view controllers will be deallocated when whatever is holding the reference to them is deallocated, in this case it would be the application itself or another controller that created the controller. If you don't hold a reference to it, it will get deallocated as soon as you exit the code block though.
Here's a little guide on using ARC
Inside an initialization method, I have the following code
- (id)init {
self = [super init];
if (self) {
UIButton *tempButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
tempButton.frame = CGRectMake(0,0,300,44);
// some custom code...
self.myButton = tempButton;
}
return self;
}
Where myButton is a retained property.
I know that, for what concerns memory management rules, this method equals this other:
- (id)init {
self = [super init];
if (self) {
UIButton *tempButton = [[UIButton alloc] initWithFrame:CGRectMake(0,0,300,44)];
// some custom code...
self.myButton = tempButton;
[tempButton release];
}
return self;
}
But in this case I need to use the first "version" because the buttonType property is readonly and I cannot change it after having the button initalized.
Since I find myself using the "non init-release" version in multiple methods all over my application, and for several object (most of them are NSString), my question is: not counting in this case the assignment to the property which retains the object, when the tempButton object will be released? Maybe at the end of the method/if statement? Or will the first "version" lead to an increased memory usage, since the object is not being released right away but after a certain amount of time?
I think you're a bit confused here: in both of your snippets, you create a tempButton object, but then you're assigning it to self.myButton. At that point, both tempButton and self.myButton are pointers to the same object. Now, presumably the myButton #property you're using is a strong property, so by assigning tempButton to it, you increase its retain count, and therefore in either version of the code it would have a retain count of +1 at the end, and would not be dealloc'ed.
If, hypothetically, myButton wasn't a strong property, then there would be an error in your code, and in both cases tempButton would be prematurely released and dealloc'ed. Here's what would happen in the two cases:
In your first version, since you're getting tempButton comes from something other than an init or copy method, it gets a retain count of +1, but is autoreleased. At the end of the current iteration of the run loop, the autorelease would kick in, bringing its retain count to 0 and causing it to be dealloc'ed.
In the second version, you first get a tempButton with a retain count of 1 because it's coming from an init method. But later on you explicitly release it, bringing its retain count to 0, at which point it is immediately dealloc'ed.
the non-init method is exactly the same as:
UIButton *tempButton = [[[UIButton alloc] initWithFrame:CGRectMake(0,0,300,44)] autorelease];
so the idea is to understand more about how the auto release pool works, its very useful most of the time but u need to understand how it works incase u will use the object later on in the app.
and to note something, when u add the temp button to ur view that view will retain it, and will release it when its removed from it, u can use instruments and check the retain count of the object if u wish to view how release/retain is going on if u want to see it in action.
I am developing an iOS app with the iOS 5 SDK, Automatic Reference Counting is enabled. But I have a specific object that is being created in large numbers and must be released after a second because otherwise the device will become very slow. It looks like they are not released, as the device is very slow. Is there a way to manually release an object when ARC is enabled?
EDIT: My code, this is called 200 times a second to generate sparkles. They fade out after 0.8 seconds so they are useless after then.
int xanimationdiff = arc4random() % 30;
int yanimationdiff = arc4random() % 30;
if (arc4random()%2 == 0) {
xanimationdiff = xanimationdiff * -1;
}
if (arc4random()%2 == 0) {
yanimationdiff = yanimationdiff * -1;
}
Sparkle *newSparkle = [[Sparkle alloc] initWithFrame:CGRectMake(20 + arc4random() % 280, 20, 10, 10)];
//[newSparkle setTransform:CGAffineTransformMakeRotation(arc4random() * (M_PI * 360 / 180))]; //Rotatie instellen (was niet mooi, net sneeuw)
[self.view addSubview:newSparkle];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.8];
[newSparkle setFrame:CGRectMake(newSparkle.frame.origin.x - xanimationdiff, newSparkle.frame.origin.y - yanimationdiff, newSparkle.frame.size.width, newSparkle.frame.size.height)];
newSparkle.alpha = 0;
[UIView commitAnimations];
The sparkle object code:
#import "Sparkle.h"
#implementation Sparkle
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"sparkle.png"]]];
}
return self;
}
#end
Object* myObject = [[Object alloc] init];
myObject = nil; // poof...
EDIT: You cannot directly control when an object is released BUT you can indirectly cause it to happen. How? Remember what ARC does EXACTLY. Unlike human coding convention, ARC parses your code and inserts release statements AS SOON AS OBJECTS CAN be released. This frees up the memory for new allocations straight away, which is awesome/necessary.
Meaning, setting an object to nil, or simply allowing a variable to go out of scope ... something that CAUSES A 0 RETAIN COUNT forces ARC to place its release calls there.
It must ... because it would leak otherwise.
Just surround the section of code that is going to be called 200 times with an #autoreleasepool { ... } statement. This will cause the memory to be deallocated immediately as opposed to waiting for the control to go all the way back up the event chain to the top level autorelease pool.
I found the answer, it was actually really stupid. I didn't remove the sparkles from the superview. Now I remove them after 0.8 seconds with a timer and it performs great again :)
With ARC you cannot call dealloc, release, or retain, although you can still retain and release CoreFoundation objects (NB: you can implement dealloc methods for your own custom subclasses, but you can't call super dealloc). So the simple answer is 'no', you unfortunately cannot manually release an object when using ARC.
I'd double check you're sure they're not being released, because in theory if you no longer reference an object it should be released. What do you do with these objects once you create them? You simply create them then immediately destroy them?
Perhaps you could post the code you're using / the property declarations - are these weak or strong referenced objects?