Передача объекта Objective C в CGFunctionRef - PullRequest
2 голосов
/ 20 декабря 2011

Я пытаюсь написать CGFunctionRef, который будет выполнять функцию затенения для объекта CGShadingRef в моем проекте, который использует ARC.Я пытаюсь передать NSMutableArray (заполненный UIColors) в мой обратный вызов 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.

1 Ответ

1 голос
/ 20 декабря 2011

Вы создаете массив и используете этот массив в качестве контекста 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
}
...