2011-12-20 73 views
2

我正在尝试编写一个CGFunctionRef,它将充当我的项目中使用ARC的CGShadingRef对象的着色函数。我试图将一个NSMutableArray(用UIColors填充)传入我的CGFunctionRef回调函数。将Objective-C对象传递到CGFunctionRef

这里是我的init方法

- (void)initInternal { 
    _colors = [[NSMutableArray alloc] init]; 

    // Creating the colors in this way ensures that the underlying color space is UIDeviceRGBColorSpace 
    // and thus has 4 color components: red, green, blue, alpha 
    [_colors addObject:[UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.0f]]; // Red 
    [_colors addObject:[UIColor colorWithRed:0.0f green:1.0f blue:0.0f alpha:1.0f]]; // Green 
    [_colors addObject:[UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:1.0f]]; // Blue 
    [_colors addObject:[UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f]]; // White 
    [_colors addObject:[UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]]; // Black 

    // Define the shading callbacks 
    CGFunctionCallbacks callbacks; 
    callbacks.version = 0;      // Defaults to 0 
    callbacks.evaluate = CGShadingCallback;  // This is our color selection function 
    callbacks.releaseInfo = NULL;    // Not used 

    // As input to our function we want 1 value in the range [0.0, 1.0]. 
    // This is our position within the 'gradient'. 
    size_t domainDimension = 1; 
    CGFloat domain[2] = {0.0f, 1.0f}; 

    // The output of our function is 4 values, each in the range [0.0, 1.0]. 
    // This is our selected color for the input position. 
    // The 4 values are the red, green, blue and alpha components. 
    size_t rangeDimension = 4; 
    CGFloat range[8] = {0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f}; 

    // Create the shading function 
    _shadingFunction = CGFunctionCreate(&_colors, domainDimension, domain, rangeDimension, range, &callbacks); 
} 

这里是我的回调方法

static void CGShadingCallback(void* info, const float* inData, float* outData) { 
    // Our colors 
    NSMutableArray* colors = (__bridge_transfer NSMutableArray*)info; 
    // Position within the gradient, ranging from 0.0 to 1.0 
    CGFloat position = *inData; 

    // Find the color that we want to used based on the current position; 
    NSUInteger colorIndex = position * [colors count]; 

    // Account for the edge case where position == 1.0 
    if (colorIndex >= [colors count]) 
     colorIndex = [colors count] - 1; 

    // Get our desired color from the array 
    UIColor* color = [colors objectAtIndex:colorIndex]; 

    // Copy the 4 color components (red, green, blue, alpha) to outData 
    memcpy(outData, CGColorGetComponents(color.CGColor), 4 * sizeof(CGFloat)); 
} 

这里是我的drawRect方法

- (void)drawRect:(CGRect)rect { 
    CGRect b = self.bounds; 
    CGContextRef ctx = UIGraphicsGetCurrentContext(); 

    // Create a simple elliptic path 
    CGContextAddEllipseInRect(ctx, b); 
    // Set the current path as the clipping path 
    CGContextClip(ctx); 

    // Create our shading using the function that was defined earlier. 
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB(); 
    CGShadingRef shading = CGShadingCreateAxial(colorspace, 
              CGPointMake(CGRectGetMinX(b), CGRectGetMidY(b)), 
              CGPointMake(CGRectGetMaxX(b), CGRectGetMidY(b)), 
              _shadingFunction, 
              true, 
              true); 

    // Draw the shading 
    CGContextDrawShading(ctx, shading); 


    // Cleanup 
    CGShadingRelease(shading); 
    CGColorSpaceRelease(colorspace); 
} 

如果我只是在我的回调方法使用__bridge,然后崩溃
NSMutableArray * colors =(_ _bridge NSMutableArray *)info;带有EXC_BAD_ACCESS的 。

如果我使用__bridge_transfer,它崩溃在 CGContextDrawShading(ctx,shading);在EXC_BAD_ACCESS的drawRect中使用 。

+0

什么样的崩溃呢? Xcode控制台中是否存在异常或引发了什么? '__bridge_transfer'听起来更正确,但你如何引用/使用'colors'变量? – 2011-12-20 02:51:16

+0

该崩溃是一个EXC_BAD_ACCESS。你是什​​么意思,我如何引用变量? – 2011-12-20 02:59:17

+0

通过“引用”,我的意思是显示'CGShadingCallback'中的附加行,显示了如何使用'colors'变量....以及崩溃发生的位置等。 – 2011-12-20 03:08:05

回答

1

您创建一个数组并使用该数组作为CGFunction的上下文。

因此,该数组需要在给予该函数时保留。你可以用关键字__bridge_retained做到这一点:

CGFunctionCallbacks callbacks; 
callbacks.version = 0; 
callbacks.evaluate = CGShadingCallback; 
callbacks.releaseInfo = myReleaseCallback; 

_shadingFunction = CGFunctionCreate((__bridge_retained void *)_colors, domainDimension, domain, rangeDimension, range, &callbacks); 

然后,您必须__bridge_transfer使用绘图回调。 __bridge_transfer将价值转换为强有力的参考而不保留它(所有权转移)。这相当于释放数组。由于您的回调可能会被多次调用,因此这不是释放数组的正确位置。

当函数被销毁时,数组必须被释放。这是releaseInfo回调的目的:

static void myReleaseCallback(void *info) { 
    CFRelease(info); 
} 

你也可以用__bridge_transfer铸做到这一点,但是这是不是很优雅:

static void myReleaseCallback(void *info) { 
    NSArray *array = (__bridge_transfer NSArray *)info; 
    // now we need to do something with the array if we don't want a compiler warning 
} 
+1

**替代解决方案:**由于数组存储在强实例变量中,因此您还可以假定此变量将在“CGFunction”的整个生命周期中保留它。在这种情况下,您可以在没有所有权转移的情况下执行所有演员(即使用'__bridge')并且不需要提供释放回调。 – 2011-12-20 17:36:44

+0

这工作。看起来我必须更多地阅读ARC。谢谢您的帮助! – 2011-12-20 17:49:12