Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where and how to __bridge

I need some advice on __bridge-ing in iOS.

Hopefully the SSCCE1 below will explain the problem better than I can in words, but I need to know how I can convert a void* to an NSMutableArray*; which __bridge variation should be used (See comment in code).

Reading about the different bridges, I deduced that I would need __bridge_transfer but then I receive an EXC_BAD_ACCESS on addObject:

Ultimately, I'd like to have an array of the CGPoints in the CGPath after CGPathApply has been called.

#import <Foundation/Foundation.h>

void _processPathElement(void* info, const CGPathElement* element)
{
    NSMutableArray *array = (/* WHAT BRIDGE HERE */ NSMutableArray*) info;
    switch (element->type)
    {
        case kCGPathElementMoveToPoint:
        case kCGPathElementAddLineToPoint:
        {
            CGPoint point = element->points[0];
            [array addObject:[NSValue valueWithCGPoint:point]];
            break;
        }
        default:
            break;
    }
}

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        //Create path
        CGMutablePathRef path = CGPathCreateMutable();
        CGPathMoveToPoint(   path, NULL, 0, 0);
        CGPathAddLineToPoint(path, NULL, 1, 0);
        CGPathAddLineToPoint(path, NULL, 1, 1);
        CGPathAddLineToPoint(path, NULL, 0, 1);
        CGPathCloseSubpath(path);

        NSMutableArray *pathPoints = [NSMutableArray array];
        CGPathApply(path, &pathPoints, _processPathElement);

        NSLog(@"Points:%@", pathPoints);
    }
}

1: SSCCE

like image 674
James Webster Avatar asked Feb 13 '13 13:02

James Webster


People also ask

When to use__ bridge?

__bridge is used to transfer/convert the pointer between Core Foundation and Foundation without ownership.

What does __ bridge do?

__bridge transfers a pointer between Objective-C and Core Foundation with no transfer of ownership.


2 Answers

The documentation on the use of the bridge keyword can be found here. Specifically, I want to point out §3.2.4:

(__bridge T) op casts the operand to the destination type T. If T is a retainable object pointer type, then op must have a non-retainable pointer type. If T is a non-retainable pointer type, then op must have a retainable object pointer type. Otherwise the cast is ill-formed. There is no transfer of ownership, and ARC inserts no retain operations.

(__bridge_retained T) op casts the operand, which must have retainable object pointer type, to the destination type, which must be a non-retainable pointer type. ARC retains the value, subject to the usual optimizations on local values, and the recipient is responsible for balancing that +1.

(__bridge_transfer T) op casts the operand, which must have non-retainable pointer type, to the destination type, which must be a retainable object pointer type. ARC will release the value at the end of the enclosing full-expression, subject to the usual optimizations on local values.

The pointer you're being passed in (void*) is a non retainable pointer type, whereas your NSMutableArray is a retainable pointer type. This rules out __bridge_retained straight away. So the question is, to __bridge or to __bridge_transfer?

__bridge_transfer is typically used when you want the Objective-C pointer from a method that returns a CF Object that has been retained. For example, CFStringCreateWithFormat will return a retained CFString, but if you want an NSString from it, you need to __bridge_transfer between them. This will make ARC release the object that CF retained when appropriate. For example, NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

Your code isn't doing that, you don't need to meddle with the ownership. Your main method is in control of its memory management, and is simply passing a reference to a method it calls (albeit indirectly, but it's all within the scope of main). As such, you would use __bridge.

But wait, when I use __bridge, my code gets memory access errors!?

Ah, this is an issue with the code you posted, and isn't in relation to the whole bridging discussion. You need to pass a void* to CGApplyPath, for your processing function _processPathElement. What you're passing is NSMutableArray**.

When you recast to the NSMutableArray*, you're actually casting a NSMutableArray**. This will cause the infamous EXC_BAD_ACCESS. You need to pass the pointer itself, not a pointer to a pointer. But, CGPathApply(path, pathPoints, _processPathElement) will not work, you cannot pass off a NSMutableArray* as a void*. What you need (ironically), is a bridge. For the same reasons as before, all you need is __bridge. See below the code, with the correct bridges in place, and working as expected:

#import <UIKit/UIKit.h> #import <Foundation/Foundation.h>  void _processPathElement(void* info, const CGPathElement* element) {     NSMutableArray *array = (__bridge NSMutableArray*) info;     switch (element->type)     {         case kCGPathElementMoveToPoint:         case kCGPathElementAddLineToPoint:         {             CGPoint point = element->points[0];             [array addObject:[NSValue valueWithCGPoint:point]];             break;         }         default:             break;     } }  int main(int argc, char *argv[]) {     @autoreleasepool     {         //Create path         CGMutablePathRef path = CGPathCreateMutable();         CGPathMoveToPoint(   path, NULL, 0, 0);         CGPathAddLineToPoint(path, NULL, 1, 0);         CGPathAddLineToPoint(path, NULL, 1, 1);         CGPathAddLineToPoint(path, NULL, 0, 1);         CGPathCloseSubpath(path);                  NSMutableArray *pathPoints = [[NSMutableArray alloc] init];         CGPathApply(path, (__bridge void*)pathPoints, _processPathElement);                  NSLog(@"Points:%@", pathPoints);     } } 

This will print out:

Points:(     "NSPoint: {0, 0}",     "NSPoint: {1, 0}",     "NSPoint: {1, 1}",     "NSPoint: {0, 1}" ) 
like image 172
WDUK Avatar answered Sep 21 '22 14:09

WDUK


I'm not actually sure why this works, but I've found the solution to be:

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

CGPathApply(path, (__bridge void*)pathPoints, _processPathElement);

If anyone can explain why this works and confirm that there aren't (/are) any memory leaks I'd be grateful

like image 42
James Webster Avatar answered Sep 22 '22 14:09

James Webster