2011-12-20 2 views
2

Я пытаюсь написать CGFunctionRef, который будет действовать как функция затенения объекта CGShadingRef в моем проекте, который использует ARC. Я пытаюсь передать NSMutableArray (заполненный UIColors) в свой обратный вызов CGFunctionRef.Передача объекта Objective-C в CGFunctionRef

Вот мой метод инициализации

- (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); в drawRect с EXC_BAD_ACCESS.

+0

какого рода аварии это? есть ли в консоли Xcode исключение или что-то еще? '__bridge_transfer' звучит более правильно, но как вы пытаетесь ссылаться/использовать переменную' colors'? –

+0

Авария - это EXC_BAD_ACCESS. Что вы имеете в виду, как я пытаюсь ссылаться на переменную? –

+0

«reference», я имею в виду показать дополнительные строки в 'CGShadingCallback', которые показывают, как используется переменная' colors' ... и где произошел сбой, и т. Д. –

ответ

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') и не обязательно предоставлять обратный вызов. –

+0

Это сработало. Похоже, мне приходится больше читать ARC. Спасибо за помощь! –

Смежные вопросы