On Nov 12, 2010, at 2:42, Peter Lübke wrote: Am 11.11.2010 um 20:26 schrieb James Dempsey:
On Nov 11, 2010, at 9:24 AM, Peter Lübke wrote:
Am 11.11.2010 um 17:16 schrieb Christiaan Hofman:
On Nov 11, 2010, at 13:04, Peter Lübke wrote:
In my app, I call AXUIElementCopyAttributeNames() and AXUIElementCopyAttributeValue() very frequently, but as far as I can see, the resulting objects are all properly released and ObjectAlloc and MallocDebug show no indication of possible leaks In your code, do you call CFRelease() on the attribute names and the attribute value when you are finished with them? The convention for Core Foundation functions like these is that, if the function contains the word "copy," you are responsible for CFReleasing the object. This applies to the accessibility functions.
To be exact, I do it like this:
// -valueForAttributeCopy: // Returns an object typed id representing the value for a given attribute of the receiver's AXUIElement or nil if the attribute doesn't exist. // The sender is responsible for releasing the object. - (id)valueForAttributeCopy:(CFStringRef)attribute { id value = nil; id axValue = nil; NSArray *attrNames;
if (AXUIElementCopyAttributeNames((CFTypeRef)_element, (CFArrayRef *)&attrNames) == kAXErrorSuccess) { if ( [attrNames indexOfObject:(NSString *)attribute] != NSNotFound && AXUIElementCopyAttributeValue((CFTypeRef)_element, attribute, (CFTypeRef *)&axValue) == kAXErrorSuccess ) value = axValue; } [attrNames release];
return value; } (where _element is an instance variable with an AXUIElementRef, stored in my AXUIElement wrapper class object.)
This may in fact point to the problem, but you don't say anything about it. You are holding on to the _element, which may lead to the corresponding object in the other app being retained. have you tried instead recreating it rather than putting it in an ivar?
Christiaan
I start iterating over the tree of UIElement children beginning with the application element. This I get with AXUIElementCreateApplication(). Once I get the array of children, I release the parent element, iterate over the children array in the same manner and so on. Every parent is released when I'm done with it. When my scanning method finds a matching element, this element, which is an instance of my wrapper class, is the only object to stay, if not, there's no object left. Everything else is released and releases its _element ivar in its -dealloc method. The reason I put the AXUIElementRef in an ivar is that I want to send action messages to it after retrieving it, and then, again, the wrapper object is released. Thus, you could say, this is a way of "recreating" the UIElement...
Anyway, how can a wrapper class work if it doesn't keep a reference to what it is refering to?
BTW, in most cases, scanning the other app is done looking for a given value for kAXTitleAttribute.
Retaining or not retaining an AXUIElementRef in your process has no any effect whatsoever on the memory management of the corresponding object in the source application.
It sounds as if there is an issue in Tiger, possibly with Mail, or possibly with menus in general (or menus with images ?), where accessing through accessibility is causing them to not be released properly. Since we are now two OS releases beyond Tiger, I don't think there is much of an opportunity for the issue to be addressed in Tiger.
It sounds like in your investigation that actually displaying the menu does cause the correct thing to happen I don't know that there is an elegant workaround or fix for this on Tiger. Perhaps you can avoid the particular menu - or scan it only once. (If it is the Mailboxes menu, then it is probably relatively static - you might be able to use notifications on the source list to try to catch new or deleted mailboxes, and rescan that menu very sparingly).
-James
"... It sounds like in your investigation that actually displaying the menu does cause the correct thing to happen - do you see the memory decrease when you do this?..."
Yes I do.
The displaying point could be what I was looking for! Am I correct in that menu items are displayed when the menu is revealed?
By itself this is obvious, of course they are "displayed when the menu is revealed" (you see them on the screen, right?). I guess that's not what you're really asking though. (at the same time they are enabled / disabled). If this is true, they are displayed exactly when I click on them.
Menus can be recreated or updated dynamically each time they are shown, through the NSMenuDelegate method menuNeedsUpdate:. This is of course app-specific. One thing I can imagine is that showing the menu uses this correctly, but it's not done correctly through accessibility. If Mail does something like Christiaan said: "...It could be that some DO proxy object is legitimately holding on to the object for efficiency... " , maybe Mail holds on to the all accessibility objects an assistive app has shown interest in. It releases the objects at the point it sends a -display message to the views under the menu bar.
Is there any way to make other apps display their views or mark them as dirty, maybe by sending AppleEvents?
I'll try to verify if Mail duplicates the objects iterated over with every iteration by implementing a counter in my test app.
The reason why I need to scan apps for several times: I want to give the user the option to locate an UIElement in all currently running apps, for example by typing the title of an element he vaguely remembers. If there's no matching element, he's likely to give it another try, without interacting with the menus of all running apps in between...
I also have the idea of trying to use the services menu for a more transparent workaround, so the user can see my app is scanning. This might take some days, I'll report results.
Thank you all for your great helping efforts!
Peter
I think it's hard to figure out what really is happening. It seems clear that at least there is no real leak. Most likely just being inefficient in thos OS version. There is probably not much (nothing) you can do about it, other than upgrading your system.
If you want to get more into what really goes on, you may want to build a simple test app where you can control exactly what happens, rather than relying on a black box like Mail.app.
Christiaan
|