SEVERE memory leaks using NS classes? Is it invetible?

Discussion in 'iOS Programming' started by antago, Nov 1, 2010.

  1. antago macrumors newbie

    Joined:
    Nov 1, 2010
    #1
    I've noticed some really serious issues with using any of the NS classes. The problem is that if you are trying to keep track of retain counts, you SIMPLY CANNOT because instantiating objects using another object AUTOMATICALLY creates a reference to it and bumps its retain count.

    Take, for instance, the following code

    Code:
    	- ( BOOL )load:( NSURL * )purl
    	{
    		fileurl = purl;
    		NSLog( @"%d", [ fileurl retainCount ] );
    		
    		NSURLRequest* request = [ NSURLRequest requestWithURL:purl ];
    		NSLog( @"%d", [ fileurl retainCount ] );
    		NSURLConnection* conn = [ NSURLConnection connectionWithRequest:request delegate:self ];
    		NSLog( @"%d", [ fileurl retainCount ] );
    		
    		if ( conn )
    		{
    			data = [ [ NSMutableData alloc ] init ];
    		}
    		else
    		{
    			[ conn release ];
    			NSLog( @"Failed to connect to file: %@", filename );
    			
    			return FALSE;
    		}
    		
    		return TRUE;
    	}
    	
    	- ( void )connection:( NSURLConnection * )pconnection didReceiveResponse:( NSURLResponse * )response
    {
    	NSLog( @"%d", [ fileurl retainCount ] );
    		[ data setLength:0 ];
    		NSLog( @"Loading file: %@", filename );
    	}
    	
    	- ( void )connection:( NSURLConnection * )pconnection didReceiveData:( NSData * )pdata
    	{
    		[ data appendData:pdata ];
    		NSLog( @"Downloading data... %d", [ data length ] );
    	}
    	
    	- ( void )connectionDidFinishLoading:( NSURLConnection * )pconnection
    	{
    		[ self process ];
    		NSLog( @"Succeeded! Received %d bytes of data", [ data length ] );
    		NSLog( @"%d", [ fileurl retainCount ] );
    	}
    
    A URL is passed to the method "load" and the url is stored in a class property I named "fileurl". That's fine. The output of the following NSLog is as expected: 1
    So, I call "NSURLRequest" and its "requestWithURL" method, and the retain count bumps up to "2". Fine. It makes sense because the object probably needs to use the URL, and I expect the coders of the class to release my URL when it is done with it.

    Problem is, the next NSLog outputs are as follows, "5", "10", and "10". Wow, this is really fantastic. Hundreds of built-in classes for us to use and NONE of them cleanup their references. NONE.



    I've even used this code:
    Code:
    		UIImage* bitmap = [ [ UIImage alloc ] initWithData:data ];
    		[ bitmap release ];
    
    ANY, and I mean EVERY class included in XCode I've used that has an "init" method where you pass an object to it--it not only bumps the retain count of that object to keep it, but has no intention of cleaning that up. Instead they are automatically retained and you must release the object yourself.

    For instance, the following code outputs as follows:
    Code:
    		NSLog( @"data... %d", [ data retainCount ] );
    		UIImage* bitmap = [ [ UIImage alloc ] initWithData:data ];
    		NSLog( @"data... %d", [ data retainCount ] );
    		image = [ [ UIImageView alloc ] initWithImage:bitmap ];
    		NSLog( @"data... %d", [ data retainCount ] );
    		[ bitmap dealloc ];
    		NSLog( @"data... %d", [ data retainCount ] );
    		[ image dealloc ];
    		NSLog( @"data... %d", [ data retainCount ] );
    
    2010-11-01 16:12:30.128 Keeola[16629:207] data... 1
    2010-11-01 16:12:30.129 Keeola[16629:207] data... 2
    2010-11-01 16:12:30.130 Keeola[16629:207] data... 2
    2010-11-01 16:12:30.130 Keeola[16629:207] data... 2
    2010-11-01 16:12:30.131 Keeola[16629:207] data... 2

    Meanwhile, the authors of the "UIImage" class appear to attempt to cover up their poor management skills by adding a release of the "data" object IF you do NOTHING with it. For instance,
    Code:
    		NSLog( @"data... %d", [ data retainCount ] );
    		UIImage* bitmap = [ [ UIImage alloc ] initWithData:data ];
    		NSLog( @"data... %d", [ data retainCount ] );
    		[ bitmap dealloc ];
    		NSLog( @"data... %d", [ data retainCount ] );
    
    Outputs
    data... 1
    data... 2
    data... 1

    So, ya, deallocating a UIImage object when I make absolutely no use of it does indeed release the "NSData" object sent to it, but there's not much good considering you aren't viewing the object.

    Am I missing something here? I'm following the rules of keeping track of memory, but when I start trying to do anything that's worth anything, suddenly my memory is getting tossed around and stolen by god knows what. How do you POSSIBLY manage memory?
     
  2. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    I haven't read all of that but, in short, the problem is with you. Hundreds of thousands of programmers are using the NS (and UI) classes without any memory leaks.
     
  3. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #3
    Never call dealloc. You call release when you're done with the object and dealloc will be called by the framework for you when appropriate.

    retainCount is often not useful for debugging memory leaks. There are too many objects that work behind the scenes that retain objects and there are also cases where an object is both retained and autoreleased to control the lifetime of the object. It's difficult or impossible to understand all these cases because you don't see all the code.

    What you should do is learn the memory management rules. Then use them correctly. Then use build and analyze and the leaks tool to find problems.

    Nothing that you show is proof of any error or leak in the Foundation classes.
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    Unfortunately, you're NOT following the rules. The rules DO NOT say to call dealloc when you're done with an object you own. The rules DO say to call release. Calling dealloc is very very different, and very very wrong. Every single use of dealloc you've shown is simply wrong.
     
  5. antago thread starter macrumors newbie

    Joined:
    Nov 1, 2010
    #5
    Well I'm glad you clarified that, and it is a relief. I didn't think dealloc was necessary, but I know that releasing objects which have retained another variable doesn't necessarily release that variable...without knowing the in depth processes of all the NS classes, I can only speculate.

    What are the guidelines for initializing an object and releasing it in these cases? I can keep track of all of my own resources. Such as the example I've shown, instantiating a UIImage using imageWithData retains my NSData object. Then, when I add my UIImage to the UIImageView, releasing UIImage and UIImageView does NOT release NSData. So, where is NSData being held and why does it appear to not be released?

    Am I correct to be concerned? The Apple class reference makes no note of the object being retained, nor does it recommend I release it manually after passing it. Is it safe to say it will be released on its own without me needing to do anything with it? Is that correct semantics?
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    Post the code that demonstrates what you know (underlined above). The code you've posted so far does not demonstrate this, because you're calling dealloc instead of release. If you want to modify what you've posted so it uses release, it must first use release correctly. You can then have it display retainCount, and use the displayed values to demonstrate your point. Posting incorrect code can't possibly demonstrate your point.

    Neither you nor your objects should care what happens inside any other objects or classes, as long as the other classes follow the rules correctly. Doing so violates the encapsulation principle.
    http://en.wikipedia.org/wiki/Encapsulation_(computer_science)


    http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html


    The code you posted involving a UIImage and UIImageView didn't use release, it used dealloc and did so wrongly.

    Calling dealloc might not alter an object's retain count, because only retain and release should alter retain count. At the time the last dealloc method is called (when called correctly), the value of retain count is irrelevant: the entire object is about to be deallocated, so the contents of its memory allotment is no longer of any concern. This is why you can't rely on retain count going to zero after an object is dealloc'ed. After you release an object, you've given up all ownership of it, so any uses of it (which would be necessary to get its retain count), are invalid uses.
     
  7. antago thread starter macrumors newbie

    Joined:
    Nov 1, 2010
    #7
    Okay, I don't actually even use dealloc. I only used dealloc as an extreme example, as most dealloc functions are (and should be) overrode in a class to cleanup any variables/memory they retain (to release them).

    REPLACE ALL INSTANCES OF DEALLOC WITH RELEASE, then try out a code using it.


    Code:
    Code:
    		NSLog( @"data %d", [ data retainCount ] );
    		UIImage* bitmap = [ [ UIImage alloc ] initWithData:data ];
    		NSLog( @"data %d", [ data retainCount ] );
    		
    		image = [ [ UIImageView alloc ] initWithImage:bitmap ];
    		[ image release ];
    		[ bitmap release ];
    		
    		NSLog( @"data %d", [ data retainCount ] );
    
    Output:
    2010-11-01 21:08:15.274 Keeola[17396:207] data 1
    2010-11-01 21:08:15.274 Keeola[17396:207] data 2
    2010-11-01 21:08:15.275 Keeola[17396:207] data 2

    The data variable is being incremented not released. I used "dealloc", perhaps erroneously, as an extreme example that there appears to be something wrong with the class that it wasn't cleaned up. Calling even "release" has not solved this problem.
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    You assume there's a problem simply because something doesn't work the way you expected.

    The reference doc for NSObject's retainCount method says:

    Important: This method is typically of no value in debugging memory management issues. Because any number of framework objects may have retained an object in order to hold references to it, while at the same time autorelease pools may be holding any number of deferred releases on an object, it is very unlikely that you can get useful information from this method.
    (Underlines added for emphasis.)
    http://developer.apple.com/library/...ml#//apple_ref/occ/intfm/NSObject/retainCount

    Since you didn't write the classes, you don't know what should be happening inside that code. If you don't know with certainty what should be happening, how do you know if it's a problem?

    For example, how do you know that the objects you released aren't using autorelease internally? Or how do you know it's not caching some objects internally?
     
  9. antago thread starter macrumors newbie

    Joined:
    Nov 1, 2010
    #9
    Hm, okay. I don't know! That's the issue. I'm new to Objective C, so I am trying to learn how to be the most efficient with my memory and not pass any variables to any functions that might retain them and somehow neglect to call a function on that object which is required to allow it to release what was passed previously.

    I'll play it safe for now and only release objects I've retained.
     
  10. Luke Redpath macrumors 6502a

    Joined:
    Nov 9, 2007
    Location:
    Colchester, UK
    #10
    Stop right there. Without even reading the rest of your post, if you are tracking retain counts, you're doing it wrong.

    Follow the simple Memory Management Rules, which do not involve tracking retain counts (you should never rely on it) and it's hard to go wrong.
     
  11. braves4life macrumors newbie

    Joined:
    Oct 12, 2007
    #11
    That's not playing it safe... That's exactly what you're supposed to do. Notice that the memory management guidelines do not make any mention of retain counts. You should never need to worry about the retain count of an object as long as you follow the rules.
     
  12. Luke Redpath macrumors 6502a

    Joined:
    Nov 9, 2007
    Location:
    Colchester, UK
    #12
    You should also release objects you've created using an alloc/init pair, copy, or with a factory method beginning with the word 'new' (which by convention does not return an autoreleased object).
     
  13. antago thread starter macrumors newbie

    Joined:
    Nov 1, 2010
    #13
    So, if I add an item as a subView, do I HAVE to call its "removeFromSuperview" function and/or manually call a release on its superview? Or, can I simply release any of the two objects later and trust internally all of those things will happen?
     
  14. Luke Redpath macrumors 6502a

    Joined:
    Nov 9, 2007
    Location:
    Colchester, UK
    #14
    Yes. A view will retain it's sub-views and release them when it is de-allocced. If you do not need a reference to the sub-view once you've added it to it's superview, you can release it straight away. If you created the parent view, it's also your job to release it when you're done with.
     

Share This Page