AXUIElementRef and ARC - Deallocated instances and __bridge vs __bridge_transfer - objective-c

I get this error on an NSMutableArray:
-[Not A Type release]: message sent to deallocated instance 0x1006e29c0
It happens on this line:
[_array removeAllObjects];
Now, I understand what the error means but not why it happens in this case.
I've added an NSLog right before the above line like so:
NSLog(#"%#", [_array class]);
And this executes normally and with the correct behavior, this is the log:
2013-03-13 11:19:27.366 App[66921:303] __NSArrayM
2013-03-13 11:19:27.367 App[66921:303] *** -[Not A Type release]: message sent to deallocated instance 0x1006e29c0
So right before removing it doesn't seem to be deallocated..
Even if I remove the removeAllObjects call and replace it with the below line, I still get the same error.
_array = [[NSMutableArray alloc] init];
All I'm doing elsewhere in the code is calling [_array addObject:...]
_array is a strong property:
#property (strong) NSMutableArray *array;
The array is first initialized in the init method.
EDIT: I'm adding AXUIElementRefs to this array like so:
[_array addObject:(__bridge_transfer id)elementRef];
I thought the __bridge_transfer brings it over to ARC and I wouldn't need to manage any of that?
What could be the problem?
UPDATE:
Here is a sample project: http://users.telenet.be/prullen/AXMemory.zip
This is a working project as I'm using __bridge and not __bridge_transfer.
This project contains a simple loop that does the same thing over and over again. This is to demonstrate that with just __bridge, the AXUIElementRefs never get released. They stay in memory. That's what I see in the Instruments profiler too. You can also see the memory usage increasing every few seconds via activity monitor.
The AXUIElementRefs were obtained via AXUIElementCopyMultipleAttributeValues - so I would assume that I would have ownership of them? Nevertheless, changing to __bridge_transfer results in the errors above.
If anyone could take a look at this and let me know what I'm doing wrong, that would be much appreciated.
I get an array of properties via the following statement: (line 60).
AXUIElementCopyMultipleAttributeValues(frontWindowRef, attributes,0,&attributeValues);
Line 110 I assign the AXUIElement (looping over the children array):
element = CFArrayGetValueAtIndex(childrenArrayRef, i);
Line 112 is where I add the AXUIElementRef to the array:
[_array addObject:(__bridge id)(element)];
Line 36 is where I empty it if it's over 500 elements:
if ([_array count] > 500) {
[_array removeAllObjects];
}

You can't just assume that __bridge_transfer "brings it over to ARC and [you] wouldn't need to manage any of that". You have to understand what rights and responsibilities you had for elementRef at the time (which we can't tell from what you've written).
I find it's much easier to understand the bridge casts if you use the corresponding CFBridgingRetain() and CFBridgingRelease() functions. __bridge_transfer is the same as CFBridgingRelease(). So, the question is: do you own elementRef at the time? Is it appropriate for you to release it?
Consider, would the following have been correct?
[_array addObject:(__bridge id)elementRef];
CFRelease(elementRef);
If not, then neither is __bridge_transfer.
From the nature of the error you're getting, I strongly suspect you were not entitled to release elementRef, so you were not entitled to tell ARC that it owns it and must release it. You have caused the AXUIElementRef to be over-released. You should just have used a __bridge cast – no "transfer" and no CF…Release().

Related

Printing element removed from nsarray

I am playing with objective-c and did sample test.
In non-arc environment I have the following code:
NSNumber * one;
NSLog(#"At first one retain count is: %d",[one retainCount]);
one = [[NSNumber alloc]initWithInt:5];
NSNumber * two = [[NSNumber alloc]initWithInt:1];
NSNumber * three =[[NSNumber alloc]initWithInt:2];
NSLog(#"After initializing %d",[one retainCount]);
NSMutableArray * array = [NSMutableArray arrayWithObjects:one,two,three, nil];
NSLog(#"After adding to array it is: %d",[one retainCount]);
NSString * a = [array objectAtIndex:0];
NSLog(#"Assigning to variable a: %d",[one retainCount]);
NSLog(#" %# ",a);
[one release];
[array removeObjectAtIndex:0];
NSLog(#"after releasing and removing from array %d",[one retainCount]);
NSLog(#" %# ",a);
It seems like the I wouldn't be able to use nslog the variable a but I can do it.
I am getting the following output;
2013-03-07 17:57:09.706 Interview[33491:11303] At first one retain count is: 3
2013-03-07 17:57:09.707 Interview[33491:11303] After initializing 2
2013-03-07 17:57:09.707 Interview[33491:11303] After adding to array it is: 3
2013-03-07 17:57:09.707 Interview[33491:11303] Assigning to variable a: 3
2013-03-07 17:57:09.707 Interview[33491:11303] 5
2013-03-07 17:57:09.707 Interview[33491:11303] after releasing and removing from array 1
2013-03-07 17:57:09.708 Interview[33491:11303] 5
How it's possible that I can still use the variable a even if I it supposed be deallacated?
You'll get told many times you should not use retainCount, but your question is:
How it's possible that I can still use the variable a even if I it supposed be deallacated?
When an object is deallocated that simply means that the memory that was used for it is added to the pool of available memory. The memory previously in use is not completely overwritten to remove all traces of what was stored in it.
Furthermore when you invoke [one release] this only indicates you no longer have an interest in the object referenced by the value stored in one (and as a result that object's memory may be returned to the free pool) it does not alter the value stored in one itself. So after the call one still contains the reference to where the object was, or still is...
If you wish to make sure you don't use an out-of-date reference you can use code like:
[one release]; one = nil;
Finally you report that before you even allocate your object your first NSLog outputs:
2013-03-07 17:57:09.706 Interview[33491:11303] At first one retain count is: 3
How can you have a retain count of 3 when you've never even allocated an object? The answer is you are lucky your code didn't blow up; when a local variable, which your one appears to be, is created it starts with garbage contents - calling a method using that garbage as an object reference is unwise. Remember Objective-C is not a "safe" language and will not protect you from such errors. Note that instance and global variables are initialised to 0/nil, and invoking methods on nil is supported in Objective-C - you just get 0 back.
Because you can never be fully sure that your object hasn't been retained elsewhere in the framework. Be sure you obey the memory management policy, and let the framework worry about its obligations.
Additionally, don't use retainCount. Apple tells you explicitly not to use it, and it isn't guaranteed to tell the truth.
Directly from the documentation: Do not use this method.
Special Considerations
This method is 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.
To understand the fundamental rules of memory management that you must abide by, read “Memory Management Policy”. To diagnose memory management problems, use a suitable tool:
The Clang Static analyzer can typically find memory management problems even before you run your program.
The Object Alloc instrument in the Instruments application (see Instruments User Guide) can track object allocation and destruction.
Look here, this explains everything more clearly:
http://www.whentouseretaincount.com

ARC and __unsafe_unretained

I think I have a pretty good understanding of ARC and the proper use cases for selecting an appropriate lifetime qualifiers (__strong, __weak, __unsafe_unretained, and __autoreleasing). However, in my testing, I've found one example that doesn't make sense to me.
As I understand it, both __weak and __unsafe_unretained do not add a retain count. Therefore, if there are no other __strong pointers to the object, it is instantly deallocated (with immutable strings being an exception to this rule). The only difference in this process is that __weak pointers are set to nil, and __unsafe_unretained pointers are left alone.
If I create a __weak pointer to a simple, custom object (composed of one NSString property), I see the expected (null) value when trying to access a property:
Test * __weak myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (null)
Similarly, I would expect the __unsafe_unretained lifetime qualifier to cause a crash, due to the resulting dangling pointer. However, it doesn't. In this next test, I see the actual value:
Test * __unsafe_unretained myTest = [[Test alloc] init];
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: Hi!
Why doesn't the __unsafe_unretained object become deallocated?
[EDIT]: The object is being deallocated... if I try to substitute lines 2 - 3 with NSLog(#"%#", myTest); the app crashes (and an overridden dealloc in Test is being called immediately after the first line). I know that immutable strings will continue to be available even with __unsafe_unretained, and that a direct pointer to the NSString would work. I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
I am just surprised that I could set a property on a deallocated object (line 2), and that it could later be dereferenced from a pointer to the deallocated object it belonged to (line 3)! If anyone could explain that, it would definitely answer my question.
When the object is deallocated it is not zeroed. As you have a pointer to the deallocated object and the property value is stored at some offset to that pointer it is possible that storing and retrieving that property value will succeed after deallocation, it is also quite possible that everything will blow up for some reason or other.
That your code works is quite fragile, try debugging it with "Show Disassembly While Debugging" and stepping through, you'll probably hit an access violation, or take down Xcode itself...
You should never be surprised that strange things happen in C, Objective-C, C++ or any of the family; instead reserve your surprise for so few strange things happening!
Because the constant string in objc is a constant pointer to a heap address and the address is still valid.
edited after comment:
Maybe because the memory at the test objects address hasn't been overwritten and still contains that object? Speculating....
You can see when Test is deallocated by implementing its -dealloc method and adding some simple logging.
However, even if Test is deallocated immediately, the memory it occupied in RAM may remain unchanged at the time you call myVal.
#"hi!" produces a static global constant string instance that is, effectively, a singleton. Thus, it'll never be deallocated because it wasn't really allocated in the first place (at least, it really isn't a normal heap allocation).
Anytime you want to explore object lifespan issues, always use a subclass of NSObject both to guarantee behavior and to make it easy to drop in logging hooks by overriding behavior.
Nothing strange there…
You need to have at least 1 strong reference to object to keep it alive.
Test * anTest = [[Test alloc] init];
Test * __weak myTest = anTest;
myTest.myVal = #"Hi!";
NSLog(#"Value: %#", myTest.myVal); // Prints Value: (Hi)

Need advice about memory management

I have been working with Objective-C for a month approximately but regretfully I'm still a complete dummy in memory management so I need your advice. I pass an array from one file to the other like this
BidView *bidView = [[[BidView alloc] init] autorelease];
NSLog(#"%i",[bidView.seatsForTableCreated retainCount]);
bidView.seatsForTableCreated = [NSArray arrayWithArray:seats];
NSLog(#"%i",[bidView.seatsForTableCreated retainCount]);
[self.navigationController pushViewController:bidView animated:YES]; `
NSLog tells me that retain count of seatsForTableCreated has raised from zero to two. Then, when I quit the BidView screen (without doing anything with seatsForTableCreated array) I' m doing the following:
NSLog(#"%i",[seatsForTableCreated retainCount]);
[seatsForTableCreated release];
NSLog(#"%i",[seatsForTableCreated retainCount]);
it's quite unclear for me. Now NSLog tells me (both times) that retain count is 1. Then I repeat this procedure (running the same application I mean) and each time things are the same:0-2-1-1. So my questions are:
1)Why 0 to 2? Why retain count increases to 2 not to 1?
2)why then it drops to 1 without being impacted in any way?
3)Why it still remains 1 after i've released it?
4)How would you manage the memory in such a case?
Great thanks in advance
First and foremost. Don't call nor use retainCount for nothing, think about this property as private and only the OS can call. To check if you have a memory leak you should use Instruments.
Seems like you've created an autoreleasing ([NSArray arrayWithArray:seats]) object, so you can't manually release it.
And use the Allocations Instrument to really check if you have a memory leak.
My advice assumes you are using Xcode 4+ and you are not using ARC,
command+shift+B will analyse your memory management (and dead stores and such). I think you got it right. Don't worry about the retain counts so much until you get a complaint from Analyze or find leaks with Instruments. I am not sure how reliable retain counts are. I have seen comments on SO saying not to rely on them.
You are following the rules well
New, Alloc, Copy, Retain --> You will need to release this object when you are done with it.
I am also assuming in BidView.h your property is declared as
#property(nonatomic, retain) NSArray * seatsForTableCreated;
So releasing that in the dealloc method in BidView.m is good memory management
EDIT
It works when even though you don't allocate seats for table created because.
self.seatsForTableCreated = ... will retain whatever object you are setting there.
So if you have a property with (retain) in the declaration, you can consider
self.property = object;
as setting property and retaining it. The properties were added to objective-C to reduce similar code being in every class.
A property in .h
#property (nonatomic,retain) NSObject * property; // don't name your properties property..
Compiler will create 2 methods for you automatically when you #synthesize in the .m
-(void)setProperty:(NSObject*)newP
{
[newP retain]; // retains the new object so it sticks around for line 3
[property release]; // releases previous property
property = newP; // set the property to the object retained in line 1
// property is now same as newP and you are responsible for releasing it
// -(void) dealloc is where you should release it
}
// Note, the compiler may not create the exact same code as above when creating the //setProperty method. If it does, it could be subject to change.
-(NSObject*)property
{
return property;
}
I tried to figure out why Analyze isn't catching the issue when you don't release your property, but haven't. That is confusing and I want to explore it further.

Using objects I didn't allocate or initialize

I just wrote some code. It has my own custom made class. In that custom made class called 'WonderfulNumbers' I have a method that looks like this
- (NSString *)storedNumberAsString {
NSString *stringToReturn = [[NSString alloc]initWithFormat:#"Hello World"];
return [stringToReturn autorelease];
}
I obviously then #import "WonderfulNumbers" into the main and etc
then in my main I have an IBAction.
it reads like the following
-(IBAction)displaySomeText:(id)sender {
WonderfulNumbers *myNumber = [[WonderfulNumbers alloc]init];// Step 1
NSString *numberString = [myNumber storedNumberAsString];// Step 2
[textView insertText:numberString];// Step 3
//textView is a variable of NSTextView.
[myNumber release];// Step 4
}
I get the step by step process, and the logic behind this.
What I like to know and try to reassure to my self is what exactly is happening at step 2.
I understand Step 1, 3 and 4.
But step 2, I crated an NSString variable called 'numberString' and it obviously holds the object called 'myNumber' with the method described above applied to it. Makes sense.
What I want to know is, I never allocated or initialized the 'numberString' object of NSString class. How am I able to then use it.
I know I don't release it since it's never allocated .. but did I initialize it by just doing [myNumber storedNumberAsString]; ?
A slight explanation would be awesome. Thank's to those that reply.
P.S. I know that everything in objective-c is an object but just for the sake of this argument, since 'numberString' is not technically "created by allocate and init" is it right to call that a variable?
I think I know the differences between the two but just want reassurance.
Thanks.
You are initializing NSString *stringToReturn to the return value of [myNumber storedNumberAsString]. Inside storedNumberAsString you can see that it returns an NSString reference, properly allocated and all, so it's fine to use.
The key here is autorelease, which causes the object to be released automatically “sometime later” (actually when the topmost autorelease pool is released, which, unless you changed it yourself, tends to happen after each iteration of the event loop).
The convention in Objective-C is that if you alloc or retain an object, or get an object from a method whose name begins with new, copy, or mutableCopy, you are responsible for releasing it. Otherwise you can assume it will be released by someone else (e.g., later via autorelease). Since storedNumberAsString does not begin with new, copy, or mutableCopy, you don't need to release it. Meanwhile the implementation of storedNumberAsString must ensure that the object it allocs gets released -- in this case that is done by calling autorelease on it.

Debugging unexpected error message - possible memory management problem?

I am trying to debug an application that is throwing up strange (to my untutored eyed) errors. When I try to simply log the count of an array...
NSLog(#"Array has %i items", [[self startingPlayers] count]);
...I sometimes get an error:
-[NSCFString count]: unrecognized selector sent to instance 0x1002af600
or other times
-[NSConcreteNotification count]: unrecognized selector sent to instance 0x1002af600
I am not sending 'count' to any NSString or NSNotification, and this line of code works fine normally.
A Theory...
Although the error varies, the crash happens at predictable times, immediately after I have run through some other code where I'm thinking I might have a memory management issue. Is it possible that the object reference is still pointing to something that is meant to be destroyed? Sorry if my terms are off, but perhaps it's expecting the array at the address it calls 'count' on, but finds another previous object that shouldn't still be there (eg an NSString)? Would this cause the problem?
If so, what is the most efficient way to debug and find out what is that address? Most of my debugging up until now involves inserting NSLogs, so this would be a good opportunity to learn how to use the debugger.
This is a sign that the memory location at which your code is expecting your array to live has either:
Been deallocated and another variable has been allocated in the same place
Been clobbered by some bad code
My bet would be on the first one. You'll want to carefully look at where you are allocating the array and make sure that you're not allowing its retain count to reach zero.
Remember that if you're allocating the array using a convenience method (basically one that starts with array) and not either retaining it or assigning it using dot notation (e.g. self.myArray = [NSArray arrayWith...]) and a property marked retain, it will be freed possibly as soon as the method in which you allocated it returns.
TL;DR is to check where you're assigning the array and make sure you're using something like this:
self.startingPlayers = [NSArray arrayWithObjects:#"first", #"second", nil];
and not like this:
startingPlayers = [NSArray arrayWithObjects:#"first", #"second", nil];
That one's bitten me countless times, including in the middle of a presentation right after I mentioned not to do it.
What does [self startingPlayers] return? Try printing that first:
NSLog("startingPlayers is %#", self.startingPlayers);
Perhaps startingPlayers contains a bad pointer (uninitialized) or a pointer to something that has already been released (and reused for something else).