2011-12-20 72 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