Is there a way to “find mystery retains” …?

故事扮演 提交于 2019-11-29 18:57:31
Fabio

Just guessing... but you may overwrite the retain method of the custom class calling super and throwing a nice NSLog to print the call stack.


Update with the actual code from Joe

-(id) retain {
NSLog(@"%@", [NSThread callStackSymbols]);
return ([super retain]);
}

Another important detail is that [NSThread callStackSymbols] returns a NSArray of NSStrings that can be filtered and used for other purposes. For example in complex and dynamic code, to check if a method properly causes another one to fire.

NOTE: In an ARC environment you will need to first add the -fno-objc-arc to compiler flags to allow you to override retain and call super.

Ben Clayton

Instruments can show you the call stack for every malloc, release, and retain for any Obj-C object in your app with no code changes required. It works when you're using ARC, which is not the case for the solution from fabio.

It's really useful for finding those mystery retains - e.g. when an object just won't dealloc when it should.

Here's how:

  • CMD + I (Product / Profile)
  • When Instruments pops up choose 'Allocations' (NOT Leaks)
  • Your app should run.
  • Do whatever causes your mystery retains to happen.
  • Select the 'Allocation' instrument on the left-hand panel.
  • Press CMD + 1 or select the circle with the wave in it on the right. In the panel on the lower right, tick the 'Record reference counts' option. This is important, or only mallocs and frees will be recorded.
  • In the search box on the top-right of the list, type the name of your class (e.g. BCMyObject).
  • This filters the list of 'Statistics' to show how many instances of your class are currently live. The #Persistent column shows how many instances are live.
  • Click the row, and then the little arrow -> next to the class name. You'll see the breadcrumbs shows 'Statistics > Allocation Summary > BCMyobject'
  • This shows you all the instances of said class (and which ones are live).
  • Select an instance, and click the arrow again (this time by address)
  • Now you'll see 'Statistics > Allocation Summary > BCMyObject > History: 0xADDRESS' in the breadcrumps.
  • This'll list every time the object is malloc'd retained or released.
  • Now in the left panel where the 'Record Reference Counts' option was, press the icon that looks like a bar with boxes connected to it or press CMD + 3.
  • Select one of the rows and you'll see the complete call stack that led to the call.

Easy! (ish)

Place a breakpoint on custom class' retain

You could set a symbolic breakpoint on retain and then set it to the retain method of the custom class. The problem here is that retain is a method on NSObject so you will get the choice of all objective-c classes when placing the breakpoint.

In this case it would be better to overwrite the retain method of the custom class with a call to super, so it would not do anything but you could then place a breakpoint in it.

Use a breakpoint action to log the caller

To add a breakpoint action double click on the blue marker. Find the breakpoint in the list and press the + button on the right. Then choose Debugger command and add the GDB command frame 1 in this field, which will show you the caller of the retain. By this you cold log all retains and where they come from. When logging the releases in a similar way you could check what was the extra release.

It is still a bit tedious, but this is the best I can think of.

Instruments and its memory management stuff is your friend. Leaks and Zombies are two of the most valuable tools available. Use them.

Product -> Profile (or Cmd-I)

It is, unfortunately, not easily possible to programmatically determine what "owns" an object, since the idea of "object ownership" is a coding convention (unless you enable garbage collection).

Stack logging is often useful (I usually use a few breakpoints with bt;continue) but that only tells you the function that called retain, not the "bigger picture" (e.g. you might "transfer ownership" with [ivar2 release]; ivar2 = ivar1; ivar1 = nil;). Sometimes it's a UIKit leak so you don't have the source code and you really have to go digging.

If it's not a leak, however, call -release a few times and see where it crashes!

Have you try using "Build & Analyse" in Xcode?

It's great for getting the bottom of objects not being released.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!