Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do protocol_* methods do not work with Clang + modern GCC-Runtime on Linux?

I tried to switch some of my Objective-C projects from GCC to Clang on Linux. I used the GCC 4.6.2 runtime because the Clang compiler does not ship with one. The compiling and linking works, but when using the protocol_* methods they do not work.

The following example works fine with GCC but not as expected with 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;
}

The expected output of the GCC compiled program:

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

The unexpected output of the Clang compiled program:

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

What's wrong here? Is there some magical initialization code which will not be called when using Clang?

[Update]

When adding an implementation of the protocol like the following the objc_getProtocol() method works but the protocol_* methods still do not.

@interface MyInstance <MyProtocol>
@end

@implementation MyInstance

+ aClassMethod
{
    return nil;
}

- anInstanceMethod
{
    return nil;
}

@end
like image 275
Tilo Prütz Avatar asked Apr 25 '12 10:04

Tilo Prütz


1 Answers

In my tests, GCC works well with its included GNU libobjc, but Clang works better with GNUstep libobjc2.

GCC 4.6 w/ included GNU libobjc: PASS

P1
class method first:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → 0x10eda50
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → 0x10eda40
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
P2
class method first:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → 0x10eda50
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → 0x10eda40
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
done

GCC 4.6 w/ libobjc2 1.6: FAIL

P1
class method first:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → 0x6020a0
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
instance method follows:
the protocol: 0x602120
the protocol's name: MyProtocol
required: YES instance: YES → 0x6020b0
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 3.1 w/ GCC 4.6 GNU libobjc: FAIL

P1
class method first:
the protocol: 0x602080
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: 0x602080
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 3.1 w/ libobjc2 1.6: PASS

P1
class method first:
the protocol: 0x602080
the protocol's name: MyProtocol
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: 0x602080
the protocol's name: MyProtocol
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: 0x602080
the protocol's name: MyProtocol
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: 0x602080
the protocol's name: MyProtocol
required: YES instance: YES → (nil)
required: YES instance: NO → (nil)
required: NO instance: YES → (nil)
required: NO instance: NO → (nil)
done
like image 75
ephemient Avatar answered Oct 14 '22 09:10

ephemient