Подтвердить что ты не робот

Где и как __бридж

Мне нужен совет по __bridge -ing в iOS.

Надеюсь, что SSCCE 1 ниже объяснит проблему лучше, чем я могу, но мне нужно знать, как я могу преобразовать void* в NSMutableArray*; который должен использоваться вариант __bridge (см. комментарий в коде).

Читая о разных мостах, я понял, что мне понадобится __bridge_transfer, но затем я получаю EXC_BAD_ACCESS на addObject:

В конечном счете, я хотел бы иметь массив CGPoints в CGPath после того, как был вызван CGPathApply.

#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 Суб >

4b9b3361

Ответ 1

Документацию по использованию ключевого слова bridge можно найти здесь. В частности, я хочу указать в §3.2.4:

(__bridge T) op отбрасывает операнд к типу назначения T. Если T - тип сохраняемого типа объекта, то op должен иметь неконфигурируемый тип указателя. Если T - не сохраняемый тип указателя, то op должен иметь тип сохраняемого типа объекта. В противном случае бросок плохо сформирован. Передача права собственности отсутствует, и ARC не вставляет операции сохранения.

(__bridge_retained T) op выводит операнд, который должен иметь тип сохраняемого типа объекта, к типу назначения, который должен быть не сохраняемым типом указателя. ARC сохраняет значение при условии обычной оптимизации по локальным значениям, а получатель отвечает за балансировку, что +1.

(__bridge_transfer T) op выводит операнд, который должен иметь не сохраняемый тип указателя, к типу назначения, который должен быть типом сохраняемого типа объекта. ARC выдает значение в конце охватывающего полного выражения с учетом обычных оптимизаций по локальным значениям.

Указатель, который вы передаете (void*), является не сохраняемым типом указателя, тогда как ваш NSMutableArray является сохраняемым типом указателя. Это немедленно исключает __bridge_retained. Итак, вопрос в том, чтобы __bridge или __bridge_transfer?

__bridge_transfer обычно используется, когда вам нужен указатель Objective-C из метода, который возвращает объект CF, который был сохранен. Например, CFStringCreateWithFormat вернет сохраненную CFString, но если вы хотите получить NSString, вам нужно __bridge_transfer между ними. Это заставит ARC освободить объект, который CF сохранил, когда это необходимо. Например, NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

Ваш код не делает этого, вам не нужно вмешиваться в собственность. Ваш основной метод контролирует управление памятью и просто передает ссылку на метод, который он вызывает (хотя и косвенно, но все это входит в объем основного). Таким образом, вы использовали бы __bridge.

Но подождите, когда я использую __bridge, мой код получает ошибки доступа к памяти!?

А, это проблема с кодом, который вы опубликовали, и не относится ко всему обсуждению мостов. Вам необходимо передать void* в CGApplyPath для вашей функции обработки _processPathElement. То, что вы проходите, это NSMutableArray**.

Когда вы переходите к NSMutableArray*, вы фактически набрасываете NSMutableArray**. Это вызовет печально известный EXC_BAD_ACCESS. Вам нужно передать указатель сам, а не указатель на указатель. Но, CGPathApply(path, pathPoints, _processPathElement) не будет работать, вы не можете передать NSMutableArray* как void*. Что вам нужно (по иронии судьбы), это мост. По тем же причинам, что и раньше, все, что вам нужно, это __bridge. См. Ниже код, с правильными мостами на месте и работающими как ожидалось:

#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);
    }
}

Это напечатает:

Points:(
    "NSPoint: {0, 0}",
    "NSPoint: {1, 0}",
    "NSPoint: {1, 1}",
    "NSPoint: {0, 1}"
)

Ответ 2

Я не уверен, почему это работает, но я нашел решение таким:

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

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

Если кто-нибудь может объяснить, почему это работает, и подтвердите, что нет (/) утечек памяти, я был бы благодарен