web-dev-qa-db-ja.com

__bridgeの場所と方法

IOSでの__bridge- ingに関するアドバイスが必要です。

うまくいけば、SSCCE1 以下は言葉で問題を説明するよりも上手く説明しますが、void*NSMutableArray*に変換する方法を知る必要があります。 which __bridgeバリエーションを使用する必要があります(コードのコメントを参照)。

さまざまなブリッジについて読んで、私は__bridge_transferが必要だと推測しましたが、addObject:でEXC_BAD_ACCESSを受け取ります

最終的に、CGPointsが呼び出された後のCGPathCGPathApplyの配列が必要です。

#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 [〜#〜]

31
James Webster

ブリッジキーワード ここにあります の使用に関するドキュメント。具体的には、§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_は通常、保持されているCFオブジェクトを返すメソッドからObjective-Cポインターが必要な場合に使用されます。たとえば、CFStringCreateWithFormatは保持されたCFStringを返しますが、そこからNSStringが必要な場合は、それらの間で___bridge_transfer_する必要があります。これにより、ARCは適切なときにCFが保持したオブジェクトを解放します。たとえば、NSString* str = (__bridge_transfer NSString*) CFStringCreateWithFormat(...);

あなたのコードはそれをしていません。所有権をいじる必要はありません。メインメソッドはそのメモリ管理を制御し、呼び出すメソッドへの参照を単純に渡します(間接的ではありますが、すべてメインのスコープ内にあります)。そのため、___bridge_を使用します。

しかし、__ bridgeを使用すると、コードにメモリアクセスエラーが発生します!?

ああ、これはあなたが投稿したコードの問題であり、ブリッジング全体の議論とは関係ありません。処理関数_void*_に対して、__processPathElement_をCGApplyPathに渡す必要があります。渡すのは_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}"
)
_
57
WDUK

これがなぜ機能するのか実際にはわかりませんが、解決策は次のとおりです。

NSMutableArray *array = (__bridge NSMutableArray*) info;

//AND

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

誰かがこれが機能する理由を説明し、メモリリークがない(/ない)ことを確認できれば、感謝します

1
James Webster