12

Objective-C プロジェクトのいくつかを GCC から Linux 上の Clang に切り替えようとしました。Clang コンパイラには GCC 4.6.2 ランタイムが付属していないため、GCC 4.6.2 ランタイムを使用しました。コンパイルとリンクは機能しますが、protocol_*メソッドを使用すると機能しません。

次の例は、GCC では問題なく動作しますが、Clang では期待どおりに動作しません。

#include <objc/runtime.h>
#include <stdio.h>

@protocol MyProtocol
+ aClassMethod;
- anInstanceMethod;
@end

void doIt(Protocol *p, SEL sel)
{
    printf("the protocol: %p\n", p);
    if (!p) return;
    printf("the protocol's name: %s\n", protocol_getName(p));
    struct objc_method_description d = protocol_getMethodDescription(p, sel, YES, YES);
    printf("required: YES instance: YES → %p\n", d.name);
    d = protocol_getMethodDescription(p, sel, YES, NO);
    printf("required: YES instance: NO → %p\n", d.name);
    d = protocol_getMethodDescription(p, sel, NO, YES);
    printf("required: NO instance: YES → %p\n", d.name);
    d = protocol_getMethodDescription(p, sel, NO, NO);
    printf("required: NO instance: NO → %p\n", d.name);
}

int main(int argc, char **argv)
{
    Protocol *p1 = @protocol(MyProtocol);
    printf("P1\n");
    printf("class method first:\n");
    doIt(p1, @selector(aClassMethod));
    printf("instance method follows:\n");
    doIt(p1, @selector(anInstanceMethod));

    Protocol *p2 = objc_getProtocol("MyProtocol");
    printf("P2\n");
    printf("class method first:\n");
    doIt(p2, @selector(aClassMethod));
    printf("instance method follows:\n");
    doIt(p2, @selector(anInstanceMethod));

    printf("done\n");
    return 0;
}

GCC コンパイル済みプログラムの期待される出力:

P1
class method first:
the protocol: 0x804a06c
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → 0x804b530
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x804a06c
the protocol's name: MyProtocol
required: YES instance: YES → 0x804b528
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
P2
class method first:
the protocol: 0x804a06c
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → 0x804b530
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x804a06c
the protocol's name: MyProtocol
required: YES instance: YES → 0x804b528
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
done

Clang コンパイル済みプログラムの予期しない出力:

P1
class method first:
the protocol: 0x804a050
the protocol's name: (null)
required: YES instance: YES → (nil)
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x804a050
the protocol's name: (null)
required: YES instance: YES → (nil)
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
P2
class method first:
the protocol: (nil)
instance method follows:
the protocol: (nil)
done

ここで何が問題なのですか?Clang を使用するときに呼び出されない魔法の初期化コードはありますか?

[アップデート]

次のようなプロトコルの実装を追加すると、objc_getProtocol()メソッドは機能しますが、protocol_*メソッドはまだ機能しません。

@interface MyInstance <MyProtocol>
@end

@implementation MyInstance

+ aClassMethod
{
    return nil;
}

- anInstanceMethod
{
    return nil;
}

@end
4

1 に答える 1

2

私のテストでは、GCC は付属の GNU libobjc でうまく動作しますが、Clang は GNUstep libobjc2 でうまく動作します。

GNU libobjc を含む GCC 4.6: PASS

P1
最初のクラスメソッド:
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須:YES インスタンス:NO → 0x10eda50
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須:YES インスタンス:YES → 0x10eda40
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
P2
最初のクラスメソッド:
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須:YES インスタンス:NO → 0x10eda50
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須:YES インスタンス:YES → 0x10eda40
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
終わり

GCC 4.6 と libobjc2 1.6: 失敗

P1
最初のクラスメソッド:
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須:YES インスタンス:NO → 0x6020a0
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602120
プロトコルの名前: MyProtocol
必須:YES インスタンス:YES → 0x6020b0
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
P2
最初のクラスメソッド:
プロトコル: (nil)
インスタンスメソッドは次のとおりです。
プロトコル: (nil)
終わり

Clang 3.1 w/ GCC 4.6 GNU libobjc: FAIL

P1
最初のクラスメソッド:
プロトコル: 0x602080
プロトコルの名前: (null)
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602080
プロトコルの名前: (null)
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
P2
最初のクラスメソッド:
プロトコル: (nil)
インスタンスメソッドは次のとおりです。
プロトコル: (nil)
終わり

Clang 3.1 w/ libobjc2 1.6: 合格

P1
最初のクラスメソッド:
プロトコル: 0x602080
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602080
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
P2
最初のクラスメソッド:
プロトコル: 0x602080
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
インスタンスメソッドは次のとおりです。
プロトコル: 0x602080
プロトコルの名前: MyProtocol
必須: YES インスタンス: YES → (nil)
必須: YES インスタンス: NO → (nil)
必須: NO インスタンス: YES → (nil)
必須: NO インスタンス: NO → (nil)
終わり
于 2012-07-28T06:56:31.943 に答える