I am using shared delegate for getting data on URL connection. I'm getting a memory leak on my code. Can anybody please tell me what I have done wrong?
Analyser Warning:
/Users/sathish/Documents/XXX 20100908 ManageMem/Classes/Data Download/XXX DataConnect.m:68:22: warning: Potential leak of an object allocated on line 68
gXXXDataConnect = [[XXXDataConnect alloc] customInit] ;
XXXDataConnect.h
(XXXDataConnect*)sharedXXXDataConnect;
XXXDataConnect.m
(XXXDataConnect *)sharedXXXDataConnect
{
if(gXXXDataConnect == nil)
gXXXDataConnect = [[XXXDataConnect alloc] customInit] ;
return gXXXDataConnect ;
}
Well, you're never deallocating your XXXDataConnect you've just allocated. If you don't deallocate it, who will?
Assuming this is Objective-C, I think you need a return [gXXXDataConnect autorelease] as your end-of-function. http://www.otierney.net/objective-c.html#retain might be a helpful link here.
Related
I am making my SpriteKit game. When the player dies, my goal is to have the game transition back to the start screen. This is accomplished by the code below. However, I notice that the memory increases each time a new game begins. Xcode Instruments is not showing a memory leak. When the memory reaches roughly 150mb the games frame rate drops and the game become unplayable.
In the GameScene I call this function when the player dies
func gameOver(){
if let block = gameOverBlock {
worldNode.removeAllChildren()
worldNode.removeAllActions()
worldNode.removeFromParent()
self.removeAllChildren()
block()
}
}
Back in the GameViewController the following functions get called
scene!.gameOverBlock = {
[weak self] in
self!.goBack()
}
}
func goBack(){
scene!.removeFromParent()
navigationController!.popToRootViewControllerAnimated(false)
return
}
If anyone has any ideas as to how I can accomplish this without a memory leak, it would much be appreciated.
After commenting out tons of code, I have found the problem. The methods that I have posted above were not causing the leak, as Matthew suggested, there was a strong reference in the middle of my code that was stopping the ARC from releasing memory. Ill post the problem code incase anyone else may have a similar problem.
In my GameViewController, I had the following block:
scene!.zoomInBlock = {
self.scene!.size = CGSizeMake(self.scene!.size.width / 2, self.scene!.size.height / 2)
}
The correct way (without causing a strong reference) to write this would be:
scene!.zoomInBlock = {
[unowned self] in self.scene!.size = CGSizeMake(self.scene!.size.width / 2, self.scene!.size.height / 2)
}
So I'm not sure if this is an issue with Unity or with the Facebook Unity SDK, or something I might be doing? It only started appearing recently, it was working perfectly fine up until I had to update Unity for iOS9 font issues.
The point at which it crashes in Xcode is:
+ (instancetype)instanceWithRequestID:(int)requestID
{
FBUnitySDKDelegate *instance = [[FBUnitySDKDelegate alloc] init];
instance->_requestID = requestID;
[g_instances addObject:instance]; // Breaks on this line. instance is nil
return instance;
}
And the code I am using for the AppRequest is
public void RequestLivesFromFriends(string[] friendIds)
{
if(!FB.IsLoggedIn)
{
LoginToFacebook ();
return;
}
FB.AppRequest(
"Please send me a life!",
Facebook.Unity.OGActionType.ASKFOR,
livesIdValue,
friendIds,
"RequestLife",
"Request a life from your friends",
requestLifeCallback
);
}
Is there currently an issue with the SDK's? Or am I just doing something wrong?
Well, I found the solution myself in the end.
I had -fno-objc-arc set as the Compiler Flag on FBUnitySDKDelegate.m
Apparently, having that on with the more recent versions of the SDK (or maybe something else was causing it, I'm not exactly sure) causes the NSMutableArray g_instances to be converted to an NSString. So when the code tries to add the FBUnitySDKDelegate object 'instance' to g_instances, it is trying to call addObject on an NSString, passing in an FBUnitySDKDelegate, which obviously doesn't work.
So yeah, if you have this problem, check for Compiler Flags on the file.
I have an app I call memory eater, which is meant to force other applications to be dumped by the os. It does this by consuming lots of memory over time until it is terminated due to memory pressure. In order to consume memory I basically make copies of JPEG representations of data:
-(IBAction)didTapStartButton:(id)sender{
int i = 200;
while (i>0) {
NSData* data = [UIImagePNGRepresentation(self.image) mutableCopy] ;
[self.array addObject:[[data description] mutableCopy]];
[self.array addObject:data];
i--;
}
}
This was done entirely with trial and error, and I assume that there is a more straightforward way to consume lots and lots of memory.
You can use malloc() in a loop.
while (1) {
int *ptr = malloc(4096);
assert(ptr != NULL);
*ptr = 0;
}
The *ptr = 0 line is necessary to force the page to be dirty, otherwise you will consume address space instead of memory. The number 4096 ensures that each iteration through the loop will add exactly one dirty page to the address space, since 4096 is the most common page size.
I have test code like this
- (void)viewDidLoad
{
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(test) object:nil];
[thread start];
}
-(void)test
{
MyClass *my = [[[MyClass alloc] init] autorelease];
NSLog(#"%#",[my description]);
}
I did not create any autoreleasepool for my own thread, but when the thread exit, object "my" just dealloc.why?
even though I change my test code as below
- (void)viewDidLoad
{
[super viewDidLoad];
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:#selector(test) object:nil];
[thread start];
}
-(void)test
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyClass *my = [[[MyClass alloc] init] autorelease];
NSLog(#"%#",[my description]);
}
I create my own autoreleasepool but not drain it when the thread exit. object "my" can still dealloc anyway. why?
I use Xcode5 and not using ARC
It's not documented, but the answer appears to be Yes, on OS X 10.9+ and iOS 7+.
The Objective-C runtime is open-source so you can read the source to see what's going on. The latest version of the runtime (646, which shipped with OS X 10.10 and iOS 8) does indeed add a pool if you perform an autorelease without a pool on the current thread. In NSObject.mm:
static __attribute__((noinline))
id *autoreleaseNoPage(id obj)
{
// No pool in place.
assert(!hotPage());
if (obj != POOL_SENTINEL && DebugMissingPools) {
// We are pushing an object with no pool in place,
// and no-pool debugging was requested by environment.
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
// Install the first page.
AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
setHotPage(page);
// Push an autorelease pool boundary if it wasn't already requested.
if (obj != POOL_SENTINEL) {
page->add(POOL_SENTINEL);
}
// Push the requested object.
return page->add(obj);
}
This function is called when you push the first pool (in which case the thing pushed is POOL_SENTINEL), or you autorelease with no pool. When the first pool is pushed, it sets up the autorelease stack. But as you see from the code, as long as the DebugMissingPools environmental variable is not set (it's not set by default), when autorelease is done with no pool, it also sets up the autorelease stack, and then pushes a pool (pushes a POOL_SENTINEL).
Similarly, (it's a little hard to follow without looking at the other code, but this is the relevant part) when the thread is destroyed (and the Thread-Local Storage is destroyed), it releases everything in the autorelease stack (that's what the pop(0); does) so it doesn't rely on the user to pop the last pool:
static void tls_dealloc(void *p)
{
// reinstate TLS value while we work
setHotPage((AutoreleasePoolPage *)p);
pop(0);
setHotPage(nil);
}
The previous version of the runtime (551.1, which came with OS X 10.9 and iOS 7), also did this, as you can see from its NSObject.mm:
static __attribute__((noinline))
id *autoreleaseSlow(id obj)
{
AutoreleasePoolPage *page;
page = hotPage();
// The code below assumes some cases are handled by autoreleaseFast()
assert(!page || page->full());
if (!page) {
// No pool. Silently push one.
assert(obj != POOL_SENTINEL);
if (DebugMissingPools) {
_objc_inform("MISSING POOLS: Object %p of class %s "
"autoreleased with no pool in place - "
"just leaking - break on "
"objc_autoreleaseNoPool() to debug",
(void*)obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return nil;
}
push();
page = hotPage();
}
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
But the version before that (532.2, which came with OS X 10.8 and iOS 6), does not:
static __attribute__((noinline))
id *autoreleaseSlow(id obj)
{
AutoreleasePoolPage *page;
page = hotPage();
// The code below assumes some cases are handled by autoreleaseFast()
assert(!page || page->full());
if (!page) {
assert(obj != POOL_SENTINEL);
_objc_inform("Object %p of class %s autoreleased "
"with no pool in place - just leaking - "
"break on objc_autoreleaseNoPool() to debug",
obj, object_getClassName(obj));
objc_autoreleaseNoPool(obj);
return NULL;
}
do {
if (page->child) page = page->child;
else page = new AutoreleasePoolPage(page);
} while (page->full());
setHotPage(page);
return page->add(obj);
}
Note that the above works for any pthreads, not just NSThreads.
So basically, if you are running on OS X 10.9+ or iOS 7+, autoreleasing on a thread without a pool should not lead to a leak. This is not documented and is an internal implementation detail, so be careful relying on this as Apple could change it in a future OS. However, I don't see any reason why they would remove this feature as it is simple and only has benefits and no downsides, unless they completely re-write the way autorelease pools work or something.
Apple documentation says (4th paragraph):
You create an NSAutoreleasePool object with the usual alloc and
init messages and dispose of it with drain (or release—to understand
the difference, see Garbage Collection). Since you cannot retain an
autorelease pool (or autorelease it—see retain and autorelease),
draining a pool ultimately has the effect of deallocating it. You
should always drain an autorelease pool in the same context
(invocation of a method or function, or body of a loop) that it was
created. See Using Autorelease Pool Blocks for more details.
So, this code runs properly, without causing any crashes in the iOS simulator. However, on my iOS device (my iPhone), this causes a crash! Can anyone guess why? The logs haven't helped anywhere.
NSInteger loops = 1;
char character = '.';
if ([futureTr rangeOfString:[NSString stringWithFormat:#"%c",character]].location != NSNotFound) {
NSArray *dotsArray = [futureTr componentsSeparatedByString:[NSString stringWithFormat:#"%c",character]];
loops = [dotsArray count];
if ([[dotsArray objectAtIndex:loops-1] isEqualToString:#""] || [dotsArray objectAtIndex:loops-1] == nil) {
loops--;
}
}
And I know that it's this statement that causes trouble, because I comment it out and the app just works fine! What can be going on?
Based on the discussion in the comments, the problem is that futureTr is being released at some point and this code then attempts to use the now deallocated pointer resulting in the "BAD_ACCESS" exception.
Proper memory management of this variable will alleviate the problem.