Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bridge cast required, inconsistent warning while trying to use CGPathRef

I was just configuring an CABasicAnimation for shadowPath property and this made me curious:

shadowAnimation.toValue = (id)newShadowPath.CGPath;

[note: shadowAnimation is an CABasicAnimation object and newShadowPath is an UIBezierPath object]

this works and no error/warning is visible in Xcode. However if I write that like so:

CGPathRef test = newShadowPath.CGPath;
shadowAnimation.toValue = (id)test;

This won't compile, throwing this warning message:

Cast of C pointer type 'CGPathRef' (aka 'const struct CGPath *') to Objective-C pointer type 'id' requires a bridged cast

So it requires me to type it like:

shadowAnimation.toValue = (__bridge id)test;

Now why is this so? Why don't I get the same error in the initial example when I use only (id)newShadowPath.CGPath; ? Would it be correct to place __bridge cast there too regardless of Xcode not detecting any problems? Or am I missing what's the difference here?

like image 859
Ivan Kovacevic Avatar asked Oct 04 '22 19:10

Ivan Kovacevic


2 Answers

The reason for the difference lies in the new (and a bit complicated) conversion rules introduced in clang 3.1.

First, lets boil it down:

@@interface Foo : NSObject
+ (CGPathRef)bar;
@end

CGPathRef foo();

void test()
{
    // works without bridged cast:
    id a = (id)[Foo bar];

    // needs bridged cast
    id b = (__bridge id)foo();
}

So when casting the result of a message we can omit the brdge cast while a normal function call needs it. That seems strange.

The reason for the difference lies in how ARC interprets functions and method names and derives assumptions about the retain count of so called C retainable pointer types (Core Foundation objects).

In section 3.3.2 of the ARC guide ("Conversion to retainable object pointer type of expressions with known semantics") you'll find the reason for the discrepancy:

An expression is known unretained if it is an rvalue of C retainable pointer type and it is [...] a message send [...]

and

If the cast operand is known unretained [...] the conversion is treated as a __bridge cast

The same section describes why this not applies to C functions and how they can be decorated to enable clang to make similar assumptions. So we can modify the example from above to get rid of the bridge cast for the C function call, as well:

CGPathRef foo() __attribute__((cf_returns_not_retained));

To answer your final question: It's safe to use the bridge cast in both places. It even makes sure ARC picks the right __bridge cast (it could pick the __bridge_transfer cast depending on the name of the method. In this case it would use __bridge, though).

like image 172
Nikolai Ruhe Avatar answered Oct 10 '22 11:10

Nikolai Ruhe


This is caused by ARC, and the magic that it does when assignments occur.

When you create a temporary variable CGPathRef test and assign to it, ARC sees that you're using a CoreBlank instance that doesn't follow the standard memory paradigms and doesn't retain it or do anything fancy.

Later, when you attempt to assign this non-managed instance to a retained id-typed property, LLVM freaks right out, because it thought it didn't have to manage the memory for that instance, but now it should start to (which is the intent of the __bridge keyword).

In the first code snippet, the reason it works is because ARC always knows that it should treat that instance as ARC-managed and there never exists a reference to it which isn't ARC managed.

like image 30
Ben S Avatar answered Oct 10 '22 10:10

Ben S