My project is using Automatic Reference Counting, and I'm trying to use the following Accessibility API function:
extern AXError AXUIElementCopyAttributeValue (
AXUIElementRef element,
CFStringRef attribute,
CFTypeRef *value);
To call the function, I'm doing something like this:
NSArray *subElements = nil;
AXUIElementCopyAttributeValue(..., (CFArrayRef *)&subElements);
However, ARC is throwing the following error regarding the last argument:
error: Automatic Reference Counting Issue: Cast of an indirect pointer to an Objective-C pointer to 'CFArrayRef *' (aka 'const struct __CFArray **') is disallowed with ARC
How do I resolve this?
Have you tried using an intermediate CFArrayRef, so that you can still pass a pointer to a ref (ie, a pointer to a pointer) to AXUIElementCopyAttributeValue, but can then achieve the toll-free bridge with just an ordinary cast? E.g.
CFArrayRef subElementsCFArray;
AXUIElementCopyAttributeValue(..., &subElementsCFArray);
NSArray *subElements = (__bridge NSArray *)subElementsCFArray;
Related
static NSMutableArray *words[10]= {#"word",#"word",#"word",#"word",#"word",#"word",#"word",#"word",nil};
gives warning:
"Incompatible pointer types initializing 'NSMutableArray *' with an expression of type 'NSString *'
How should I circumvent this problem?
Your code will not compile even without static
1.Remove [10]
2.Change '{...}' to '#[...]'
3.Remove nil
You should receive
NSMutableArray *words= #[#"word",#"word",#"word",#"word",#"word",#"word",#"word",#"word"];
but words will be a NSArray, not NSMutableArray since it is initializated with NSArray object. And if you will try to declare it `static' you will receive a compiler error
initializer element is not a compile-time constant
because only compile-time constant could be declared as static. NSMutableArray could not be created at compile time.
If you need static array you should use C-style array instead
static NSString *words[] = {#"word",#"word",#"word",#"word",#"word",#"word",#"word",#"word"};
I'm using ARC and I have an object whose reference must be passed to a struct:
myStruct->myObject = (__bridge void *)self;
There are cases where all standard references other than the one in this struct will have passed out of scope, but I still want the struct to keep the object's retain count from hitting 0.
Can I just do this? :
CFRetain(myStruct->myObject);
and then later when I'm destroying my struct just call
CFRelease(myStruct->myObject);
These are getting called on the void* bridged reference, and I'm not sure if they keep that actual Objective-C class alive.
Yes, that works. Note that you can also write
myStruct->myObject = CFBridgingRetain(self);
to cast the Objective-C object to a const void * and "take ownership".
I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.
I have an NSError ** stored in an array (so I can get it as such array[0]). I'm trying to cast it into a variable:
NSError * __autoreleasing *errorPointer = (NSError * __autoreleasing *)array[0];
so I can access the underlying object as *errorPointer.
However, Xcode complains that Cast of an Objective-C pointer to 'NSError *__autoreleasing *' is disallowed with ARC. Is there any way to get to this object without turning off ARC?
Neither that stub:withBlock: method or any of its supporting infrastructure could be simply stuffing a double pointer into an NSArray. The array won't take non-objects, and a pointer to an object is not an object. There's something else going on.
This obviously requires some digging into the code to figure out. Where does the value get put into the array? That's in -[KWStub processInvocation:], and it's done apparently using a method added to NSInvocation by OCMock, getArgumentAtIndexAsObject:. In that method, the invocation uses a switch to check the type of the argument that is requested, and boxes it up if necessary.
The relevant case here is the last one, where the argument type is ^, meaning "pointer". This sort of argument is wrapped up in an NSValue; therefore, the array recieved by your Block actually contains, not the double pointer itself, but an NSValue representing the outer pointer. You just need to unbox it.
That should look like this:
NSValue * errVal = array[1];
NSError * __autoreleasing * errPtr = (NSError * __autoreleasing *)[errVal pointerValue];
I have an array of views, and I need to hide them all, so I use:
BOOL shouldHideViews = YES;
[allViews makeObjectPerformSelector:#selector(setHidden:) withObject:(void *)shouldHideViews]
When I convert the code to ARC, it tells me I need some bridged cast, then I changed:
(void *)shouldHideViews
to
(__bridge BOOL)shouldHideViews
it says incompatible types casting 'int' to 'BOOL' with a __bridge cast
So how do I do it? I know I can just iterate all the views in the array, but this is not the point, I want to know in general what I should do to make this ARC compatible.
Thanks!
The other answers indicating that you can't pass YES this way are correct. There are easier solutions however:
[allViews setValue:[NSNumber numberWithBool: shouldHideViews] forKey:#"hidden"];
This works because NSArray overrides its setValue:forKey: for exactly this use.
You can also now use blocks:
[allViews enumerateObjectsUsingBlock:
^(id obj, NSUInteger idx, BOOL *stop){ [obj setHidden:shouldHideViews];}];
Or the tried and true for() loop (see #rob mayoff's answer.)
Of them, I generally just use a for loop.
you can't pass primitive types as a void pointer. You should keep the boolean variable as an instance variable and reference it in the setHidden. That way you can just do this:
shouldHideViews = YES; //declare BOOL shouldHideViews; in your .h file
[allViews makeObjectPerformSelector:#selector(setHidden) withObject:nil];
These will be rather helpful to you
How to use performSelector:withObject:afterDelay: with primitive types in Cocoa?.
Using performSelector:withObject:afterDelay: with non-object parameters
SEL performSelector and arguments
Either you should make a wrapper using NSNumber or use NSInvocation.
The object argument to makeObjectsPerformSelector:withObject: is of type id. That means it needs to point to an Objective-C object. Casting a non-object to an id is a bad idea, because the system (particularly under ARC) is allowed to do things like send the retain and release messages to an id, and that will crash if you cast YES to id.
I suggest just using fast enumeration:
for (UIView *view in allViews) {
view.hidden = shouldHideViews;
}