4
votes

I'm working on an IPad app and have been profiling it using Instruments in XCode 4. I'm profiling on the actual device itself. iOS 4.3 is installed.

Instruments tells me I have some memory leaks, the leaked objects being mainly GeneralBlock-56 ones and some GeneralBlock-1024/GeneralBlock-8192 ones. The interesting thing is that these leaks are reported only when I load up a URL in a UIWebView embedded in my app. If I comment out the loadRequest call, these leaks go away. This behavior is consistently reproducible.

The loadRequest() calls looks like this:

[webPage loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://slashdot.org"]]];

I'm not specifying any delegates for this UIWebView, by the way. The leaks do not show any Responsible Library/Responsible Frame and there is no Extended Detail in the rightmost frame.

I have tried to fiddle around with the NSURLCache settings, like so:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:nil];
[NSURLCache setSharedURLCache:sharedCache];
[sharedCache release];

This reduces a few of the GeneralBlock memory leaks, but some still remain. Any insights on this behavior are very welcome...thanks!

4

4 Answers

2
votes

This is a bug report I filed with the Apple Developer Bug Reporting tool. This seemed to be one of the big issues causing the memory leak in my case.

Summary: Passing incorrectly formatted NSURL to NSData dataWithContentsOfURL: causes memory leak in createCanonicalURL() method.

Steps to Reproduce

In any simple project, put the following lines in your controller, for example, in loadView: or viewDidLoad: methods.:

NSURL* u = [NSURL URLWithString:@"http:/portalqa01:70/Images/Leading%20out%20of%20a%20Downturn%20-%20Article%20Illustration%20-%20Large_tcm137-38905.gif"];
    NSData* data = [NSData dataWithContentsOfURL:u options:0 error:nil];

Note that the URL has only a single slash after "http:" instead of the customary two.

Expected Results: There shouldn't be any memory leak.

Actual Results: Instruments shows a memory leak.

Regression: This only occurs for some malformed URLs, the URL above is a specific example. A URL like "http:/blah/blah.png" will not cause a leak. The environments used to reproduce this were:

  • iPad 4.3 Simulator
  • iPad 2 with iOS 4.3.1

Notes: The stack trace is as below:

STACK TRACE START

   0 CFNetwork createCanonicalURL
   1 CFNetwork HTTPProtocol::_createMutableCanonicalRequest(__CFAllocator const*, _CFURLRequest const*, void const*)
   2 CFNetwork HTTPProtocol::_createCanonicalRequest(__CFAllocator const*, _CFURLRequest const*, void const*)
   3 CFNetwork HTTPProtocol::copyCanonicalRequest()
   4 CFNetwork URLConnectionLoader::copyProtocolCanonicalRequest()
   5 CFNetwork URLConnectionClient::getRequestForTransmission(unsigned char, _CFURLResponse*, _CFURLRequest const*, __CFError**)
   6 CFNetwork URLConnectionClient::_clientWillSendRequest(_CFURLRequest const*, _CFURLResponse*, URLConnectionClient::ClientConnectionEventQueue*)
   7 CFNetwork URLConnectionClient::ClientConnectionEventQueue::processAllEventsAndConsumePayload(XConnectionEventInfo<XClientEvent, XClientEventParams>*, long)
   8 CFNetwork URLConnectionClient::processEvents()
   9 CFNetwork MultiplexerSource::perform()
  10 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
  11 CoreFoundation __CFRunLoopDoSources0
  12 CoreFoundation __CFRunLoopRun
  13 CoreFoundation CFRunLoopRunSpecific
  14 CoreFoundation CFRunLoopRunInMode
  15 CFNetwork CFURLConnectionSendSynchronousRequest
  16 Foundation +[NSURLConnection sendSynchronousRequest:returningResponse:error:]
  17 Foundation -[NSData(NSData) initWithContentsOfURL:options:error:]
  18 Foundation +[NSData(NSData) dataWithContentsOfURL:options:error:]
  19 MemLeakTester -[MemLeakTesterViewController viewDidLoad] /Users/admin/IPadSpikes/MemLeakTester/MemLeakTester/MemLeakTester/MemLeakTesterViewController.m:36
  20 UIKit -[UIViewController view]
  21 UIKit -[UIWindow addRootViewControllerViewIfPossible]
  22 MemLeakTester -[MemLeakTesterAppDelegate application:didFinishLaunchingWithOptions:] /Users/admin/IPadSpikes/MemLeakTester/MemLeakTester/MemLeakTester/MemLeakTesterAppDelegate.m:27
  23 UIKit -[UIApplication _callInitializationDelegatesForURL:payload:suspended:]
  24 UIKit -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:]
  25 UIKit -[UIApplication handleEvent:withNewEvent:]
  26 UIKit -[UIApplication sendEvent:]
  27 UIKit _UIApplicationHandleEvent
  28 GraphicsServices PurpleEventCallback
  29 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  30 CoreFoundation __CFRunLoopDoSource1
  31 CoreFoundation __CFRunLoopRun
  32 CoreFoundation CFRunLoopRunSpecific
  33 CoreFoundation CFRunLoopRunInMode
  34 UIKit -[UIApplication _run]
  35 UIKit UIApplicationMain
  36 MemLeakTester main /Users/admin/IPadSpikes/MemLeakTester/MemLeakTester/MemLeakTester/main.m:14
  37 MemLeakTester start

STACK TRACE END

1
votes

I am experiencing the same issue. I am still trying to verify this, but my initial observation is that it only occurs with sites that are serving javascript. Have you observed this pattern?

1
votes

Try adding

[webView loadHTMLString: @"" baseURL: nil];

right before you release the webview. For a leak in 4.2.1 relating to displaying a PDF in a UIWebView this solves most of the leak problems for me.

1
votes

Well, I had the same issue with UIWebView - a whole bunch of leaks (General-Block56 etc) on iPad, iPad2, iPhone. Finally the following helped: reject the Nib-file and the method 'viewDidLoad'. Instead of this I've created the webview in 'loadView' programmatically:

- (void)loadView 
{ 
    self.activityView = [[[UIActivityIndicatorView alloc]    initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray] autorelease];
    self.webView = [[UIWebView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
    [self.webView addSubview: self.activityView];
    self.webView.scalesPageToFit = YES;
    self.view = self.webView;
    [self addGestures];
} 

- (void)viewWillAppear:(BOOL)animated 
{ 
      self.webView.delegate = self;
      [self.webView loadRequest:[self urlRequest]];
      [super viewWillAppear:animated];

}

At the end there's only one small leak remaining (16 bytes) right after start loading the very first page. All other pages did load without any leak. Hope this helps.