I used instruments to measure my app.It shows some memory leaks and my app is with ARC.
Here is a picture of the leak.
Question is:
1.We can see the size of memory leak is about 1KiB,most of it is smaller.Does it matter if I do not care about it?
2.We can see the address of the instance where memory leak happens,can I locate it (in the code,i suppose),so I can fix it,and how?
because there may be the use of strong instances of objects, that are not getting released. And always use weak references under blocks.
For Example
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.drawingView.center = weakSelf.center;
});
there can also be retain cycles.
Please use call trees for solution . then analyse code as well.
Related
I recently stumbled upon The Deallocation Problem in some Objective-C code. This topic was discussed before on Stack Overflow in Block_release deallocating UI objects on a background thread. I think I understand the problem and its implications, but to be sure I wanted to reproduce it in a little test project. I first created my own SOUnsafeObject (= an object which should always be deallocated on the main thread).
#interface SOUnsafeObject : NSObject
#property (strong) NSString *title;
- (void)reloadDataInBackground;
#end
#implementation SOUnsafeObject
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = #"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], #"Object should always be deallocated on the main thread");
}
#end}]
Now, as expected, if I put [[[SOUnsafeObject alloc] init] reloadDataInBackground]; inside application:didFinishLaunching.. the app crashes after 3 seconds due to the failed assertion. The proposed fix seems to work. I.e. the app doesn't crash anymore if I change the implementation of reloadDataInBackground to:
__block SOUnsafeObject *safeSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
safeSelf.title = #"Retrieved data";
safeSelf = nil;
});
sleep(3);
});
Okay, so it seems like my understanding about the problem and how it can be solved under ARC is correct. But just to be 100% sure.. Let's try the same with an UIViewController (since an UIViewController will probably fill in the role of SOUnsafeObject in real life). The implementation is almost identical to that of the SOUnsafeObject:
#interface SODemoViewController : UIViewController
- (void)reloadDataInBackground;
#end
#implementation SODemoViewController
- (void)reloadDataInBackground {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
self.title = #"Retrieved data";
});
sleep(3);
});
}
- (void)dealloc {
NSAssert([NSThread isMainThread], #"UI objects should always be deallocated on the main thread");
NSLog(#"I'm deallocated!");
}
#end
Now, let's put [[SODemoViewController alloc] init] reloadDataInBackground]; inside application:didFinishLaunching... Hmm, the assertion doesn't fail.. The message I'm deallocated! is printed to the console after 3 seconds so I'm pretty sure the view controller is getting deallocated.
Why is the view controller deallocated on the main thread while the unsafe object is deallocated on a background thread? The code is nearly identical. Does UIKit do some fancy stuff behind the scenes to make sure an UIViewController is always deallocated on the main thread? I'm starting to suspect this since the following snippet also doesn't break my assertion:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
SODemoViewController()
});
If so, is this behavior documented somewhere? Can this behavior be relied upon? Or am I just totally wrong and is there something obvious I'm missing here?
Notes: I'm fully aware of the fact that I can use a __weak reference here, but let's assume the view controller should still be alive to execute our completion code on the main thread. Also, I'm trying to understand the core of the problem here before I circumvent it. I converted the code to Swift and got the same results as in Objective-C (the fix for SOUnsafeObject is there syntactically even uglier).
tl;dr - While I can find no official documentation, the current implementation does indeed ensure that dealloc for UIViewController happens on the main thread.
I guess I could just give a simple answer, but maybe I can do a little "teach a man to fish" today.
OK. I can't find documentation for this anywhere, and I don't remember it ever being said publicly either. In fact, I have always gone out of my way to make sure view controllers were deallocated on the main thread, and this is the first time I've ever seen someone indicate that UIViewController objects get automatically deallocated on the main thread.
Maybe someone else can find an official statement, but I couldn't find one.
However, I do have some evidence to prove that it does indeed happen. Actually, at first, I thought you were not properly handling your blocks or reference counts, and somehow a reference was being retained on the main thread.
However, after a cursory look, I was interested enough to try it for myself. To satisfy my curiosity, I made a class similar to yours that inherited from UIViewController. Its dealloc ran on the main thread.
So, I just changed the base class to UIResponder, which is the base class of UIViewController, and ran it again. This time its dealloc ran on the background thread.
Hmmm. Maybe there is something going on behind closed doors. We have lots of debugging tricks. The answer always lies with the last one you try, but I figured I'd try my usual bag of tricks for this kind of stuff.
Log Notifications
One of my favorite tools to find out how things are implemented is to log all notifications.
[[NSNotificationCenter defaultCenter]
addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *note) { NSLog(#"%#", note); }];
I then ran using both classes, and didn't see anything unexpected or different between the two. I didn't expect to, but that little trick is very simple, and it has helped me tremendously in discovering how a lot of other things worked, so it's usually first.
Log Method/Message Sends
My second trick it to enable method logging. However, I don't want to log all methods, just what happens between the time the last block executes, and the call to dealloc. So, turned on method logging by adding this as the last line of the "sleeping" block.
instrumentObjcMessageSends(YES);
And I turned logging back off, with this as the first line of the dealloc method.
instrumentObjcMessageSends(NO);
Now, this C function can't be readily found in any headers that I know of, so you need to declare it at the top of your file.
extern void instrumentObjcMessageSends(BOOL);
The logs go into a unique file in /tmp, named msgSends-.
The files for the two runs contained the following output.
$ cat msgSends-72013
- __NSMallocBlock__ __NSMallocBlock release
- SOUnsafeObject SOUnsafeObject dealloc
$ cat msgSends-72057
- __NSMallocBlock__ __NSMallocBlock release
- SOUnsafeObject UIViewController release
- SOUnsafeObject SOUnsafeObject dealloc
There is not too much surprising about that. However, the presence of UIViewController release indicates that UIViewController has a special override implementation for the +release method. I wonder why? Could it be to specifically transfer the call to dealloc to the main thread?
Debugger
Yes, this is the first thing I thought of, but I had no evidence that there was an override in UIViewController so I went through my normal process. I have found when I skip steps, it typically takes longer.
Anyway, now that we know what we are looking for, I put a breakpoint on the last line of the "sleeping" block and made the class inherit from UIViewController.
When I hit the breakpoint, I added a manual breakpoint...
(lldb) b [UIViewController release]
Breakpoint 3: where = UIKit`-[UIViewController release], address = 0x000000010e814d1a
After continuing, I was greeted with this awesome assembly, which confirms visually what is happening.
pthread_main_np is a function that tells you if you are running on the main thread. Stepping through the assembly instructions confirmed that we are not running on the main thread.
Stepping further, we get to line 27, where we jump over the call to dealloc, and instead run what you can easily see is code to run a dealloc-helper on the main thread.
Can You Count on This Going Forward?
Since I can't find it documented, I don't know that I would count on this happening all the time, but it is very convenient, and obviously something they intentionally put into the code.
I have a set of tests that I run every time Apple releases a new version of iOS and OSX. I assume most developers do something very similar. I think what I would do is write a unit test, and add it to that set. Thus, if they ever change it back, I'll know as soon as it comes out.
Otherwise, I tend to think this may be one of those things that can safely be assumed.
However, be aware that subclasses may choose to override release (if they are compiled with ARC disabled), and if they do not call the base class implementation, you will not get this behavior.
Thus, you may want to write tests for any third-party view controller classes you use.
My Details
I only tested this with XCode 6.4, Deployment target 8.4, simulating iPhone 6. I'll leave testing with other versions as an exercise for the reader.
BTW, if you don't mind, what are the details for your posted example?
I've been using an NSOperationQueue and I have very strange memory issue with it.
I've tried reducing the issue to the simplest possible probleme and here I got:
in init:
_queue = [[NSOperationQueue alloc] init];
Later:
TestOperation op = [[TestOperation alloc] init];
[self.queue addOperation: op];
then in the method called by the main of the operation:
NSLog(#"I'm right here!");
If I call this thousands of times, my memory used just keep growing.
I've I only remove the NSLog from my method (thus calling an empty method) my memory don't change.
What am I doing wrong here??
When you add an operation to an NSOperationQueue, the operation queue owns the object and will be responsible of releasing it. Perhaps you are not giving enough time to the NSOperationQueue to release the memory and see the results?
For this cases you can surrounded it with an #autorelease block, but since the operation queue is the one responsible for the release of the objects I don't know if it will work. Is worth a try.
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(self.downloadQueue, ^{
NSData *data = [self downloadFromRemoteService:service];
dispatch_async(self.storeQueue, ^{
int img = [self.imageStore addImage:data];
dispatch_saync(self.renderQueue, ^{
[self renderThumbnail:img];
dispatch_async(dispatch_get_main_queue(), ^{
[[self thumbnailViewForId:img] setNeedsDisplay:YES];
});
});
});
});
}
this is the code from Apple WWDC2012 《Asynchronous Design Patterns with Blocks, GCD, and》,'self' as strong reference in blocks, Is this code all right? or how to avoid leaks in this situation?
No, self will not leak. self will however retained until after the last block has been executed. When the last block finished, the block gets deallocated which in turn releases self. At that time, and only IFF there are no other strong references to self, it will be deallocated.
Edit:
I could not resist to mention this (because the sample is from Apple himself -- take it with a grain if salt ;) )
So, at the very top there is the method downloadFromRemoteService. It's glaringly obvious that this is a network request. Network requests are inherently _asynchronous_.
One attribute of an asynchronous operation is that this operation cannot be made "synchronous" in a truly manner anymore. Once asynchronous - always asynchronous.
What's also obvious from the code sample, that the network request is oddly enough synchronous, Ohps!
What happens when wrapping a asynchronous task into a synchronous wrapper? Well, its at least "suboptimal": the calling thread will be blocked immediately until the result is available, then just return the result. That's a quite big amount of waste for resources (threads are limited and are costly to create and require a quite amount of RAM).
So, this code has a "code smell". It's a "bad programming practice". We should make this better. ;)
Objects automaticaly retained when mentioned in block. They got released when block deallocated. So this code is all right. Problems occur when your's self-object takes ownership of such blocks with self inside.
So you just need to release block when you don't need it any more.
There is a retain cycle in this code, as self retains self.downloadQueue (and the other queues), which retains all the blocks dispatched to it, including the block here, which in turn retains self when it is copied (which happens when it is dispatched to the queue).
However, it is a temporary retain cycle, because once the block is executed on the queue, the queue will (hopefully) release it, breaking the cycle.
I have an iPad app that I am testing in Instruments before beta testing. I have gotten rid of all memory leaks except one, and I can't find any information on it. I am baffled as to what to do, since my code never mentions the leaking object which is an instance of NSDecimalNumberPlaceHolder.
For sure I am using NSDecimalNumber. I create 2 decimals per user operation and each I time I run a cycle of the app (which performs some math operation on the two NSDecimalNumbers) I generate four instances of this NSDecimalPlaceHolder thing. Since I do not know how it gets created, I do not know how to release or dealloc it so as to not generate these 16 btye leaks over and over again.
Is it possible that these are not really leaks?
I have run the XCode Analyzer and it reports no issues.
What I'm doing is this:
I send a decimal number from my controller over to my model (analyzer_) which performs the operations and sends back the result.
[[self analyzer_] setOperand:[NSDecimalNumber decimalNumberWithString:anotherStringValue]];
The setOperand method looks like this:
-(void)setOperand:(NSDecimalNumber*)theOperand
{
NSLog(#"setOperand called");
operand_ = theOperand;
//[operand_ retain];
}
Note that if I don't retain operand_ "somewhere" I get a BAD_ACCESS crash. I currently retain and release it later where the operand and the previously provided operand (queuedOperand_) are operated upon. For example:
{
[self performQueuedOperation];
queuedOperation_ = operation;
queuedOperand_ = operand_;
}
return operand_;
[operand_ release];
where performQueuedOperation is:
-(void)performQueuedOperation
{
[operand_ retain];
if ([#"+" isEqualToString:queuedOperation_])
{
#try
{
operand_ = [queuedOperand_ decimalNumberByAdding:operand_];
}
#catch (NSException *NSDecimalNumberOverFlowException)
{
//viewController will send decimal point error message
}
<etc for the other operations>
}
Let me know if this is not clear. Thanks.
Try Heapshot in Instruments, see: When is a Leak not a Leak?
If there is still a pointer to the memory that is no longer used it is not a leak but it is lost memory. I use Heapshot often, it really works great. Also turn on recording reference counting in the Allocations tool and drill down. Here is a screenshot:
EDIT: guys, my question was about using the Instruments to see leaks, and the code as an example and side question, but you answered the side question and not the main problem.... Thank you for the answers, but I really need to find out how to work the Instruments with the simulator....
I am learning IOS development, In one of the codes I'm studying i think there is huge memory leak so I've tried learning how to use instruments. As i am learning right now, I am trying to use instruments with the simulator, but all the manuals i found are for connecting to a device and then using instruments and not with the simulator. every thing I've tried doesn't show any leaks in Instruments.
The app doesn't crash because i am guessing the memory leak is not that big, but when i am adding the following code it does crash, why is that, Even when i added the release every time, still crashes....what is wrong with the simulator? or with the code? working with xcode3, not 4.
for (int i = 0; i < 1000000; i++) {
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
NSLog(#"%#",testLeak);
[testLeak release];
}
And again, the app crashes and the simulator doesn't show any leaks, even when i put the "attach process" on "iPhone simulator".
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
The problem is that you are not actually allocating anything. NSString is internally smart enough to recognize that the above expression does not need to allocate anything because the constant string #"test1223" can neither mutate nor ever be deallocated. Thus, it just returns that string.
If you were to NSLog(#"%p", testLeak); you'd see the same address over and over.
Change the NSString to NSMutableString and you'll likely see the thousands of copies. Maybe; NSMutableString could be optimized to just point to the immutable copy until a mutation operation is performed (implementation detail). Or you could allocate an instance of some class of your own creation.
Keep in mind that Leaks doesn't necessarily show you all leaks; it can't because of the way it works.
For this kind of analysis, Heapshot analysis is very effective.
If it is crashing as described, please (a) post the crash log and (b) file a bug with your app (built for the simulator) attached to http://bugreport.apple.com/.
In general Instruments + Simulator will not be terribly useful; the simulator is only an approximation of what is running on the device.
[something release] doesn't actually free the memory the instant it is called - it just decreases the reference count of an object. If the count is 0, a [something dealloc] is called, and that frees the memory. I guess you are allocating memory faster than the system can free it... besides, doing 1.000.000 allocs in rapid succession instead of single huge one is probably as bad a coding practice as they come...
It may be that some stuff is getting autorelease'd, and that is using a ton of the heap. Try changing your code to this:
for (int i = 0; i < 1000000; i++) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString *testLeak = [[NSString alloc] initWithString:#"test1223"];
NSLog(#"%#",testLeak);
[testLeak release];
[pool drain];
}
Thank you all, I actualy Found the answer around 4 am yestorday...
when you want to test leaks on the emulator:
rum -> run with Performance Tool ->Leaks
If you select the simulator on the top right as the device to run the app on, It will lounch the simulator and the instruments and start the leak recorder, all in one click....
Have fun :-)
Erez