Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjC Class identifiers & compiler magic?

Tags:

objective-c

Consider:

@interface Foo : NSObject

+ (void) dump ;

@end

@implementation Foo

+ (Class) classOf1 {
    return self ;
}

+ (Class) classOf2 {
    return [Foo class] ;
}

+ (Class) classOf3 {
    return [[[Foo class] new] class] ;
}

+ (Class) classOf4 {
    return [[self new] class] ;
}

+ (Class) classOf5 {
    return [[[self alloc] init] class] ;
}

+ (Class) classOf6 {
    return [[[Foo alloc] init] class] ;
}

+ (Class) classOf7 {
    return [self class] ;
}

+ (void) dump {
    NSLog(@"classOf1 %@<0x%08.8lx>", [self classOf1], (unsigned long)[[self classOf1] hash]) ;
    NSLog(@"classOf2 %@<0x%08.8lx>", [self classOf2], (unsigned long)[[self classOf2] hash]) ;
    NSLog(@"classOf3 %@<0x%08.8lx>", [self classOf3], (unsigned long)[[self classOf3] hash]) ;
    NSLog(@"classOf4 %@<0x%08.8lx>", [self classOf4], (unsigned long)[[self classOf4] hash]) ;
    NSLog(@"classOf5 %@<0x%08.8lx>", [self classOf5], (unsigned long)[[self classOf5] hash]) ;
    NSLog(@"classOf6 %@<0x%08.8lx>", [self classOf6], (unsigned long)[[self classOf6] hash]) ;
    NSLog(@"classOf7 %@<0x%08.8lx>", [self classOf7], (unsigned long)[[self classOf7] hash]) ;
}

@end

And the output:

    2013-07-04 03:20:20.404 WC[29862:c07] classOf1 Foo<0x0002a2e4>
    2013-07-04 03:20:21.075 WC[29862:c07] classOf2 Foo<0x0002a2e4>
    2013-07-04 03:20:21.628 WC[29862:c07] classOf3 Foo<0x0002a2e4>
    2013-07-04 03:20:22.229 WC[29862:c07] classOf4 Foo<0x0002a2e4>
    2013-07-04 03:20:22.805 WC[29862:c07] classOf5 Foo<0x0002a2e4>
    2013-07-04 03:20:23.387 WC[29862:c07] classOf6 Foo<0x0002a2e4>
    2013-07-04 03:20:25.235 WC[29862:c07] classOf7 Foo<0x0002a2e4>

All 7 cases return the exact same value!

The most puzzling cases for me:

  • classOf1 and classOf7

in the latter `self' "represents" Foo which is a class. So what's the class of a class?

  • classOf5 and classOf6

Clearly "Foo" and "self" play the exact same role in this context. As they do in

  • classOf3 and classOf4

It looks to me that "Foo" is a bit of magic bunny.

You can

@class Foo ;

In a way that

@class self ;

wouldn't make sense.

In the lexer to runtime generator pipeline, I can make sense of when a stream of bits changes from a bunch of character to an identifier to a language dependent semantic element. But I have a hard time making out the precise nature of ObjC class identifiers.

Anyone?

(This SO Question seems unrelated)

like image 452
verec Avatar asked Jul 04 '13 02:07

verec


2 Answers

You can see the answer in NSObject's implementation:

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}

Sending class to a class object doesn't return its metaclass, just the class object itself.

If you want the metaclass object for some reason, you have to use the runtime function object_getClass(), like so:

NSLog(@"%d", class_isMetaClass(object_getClass([NSString class])));
// Prints 1

You can also get the metaclass for a class name with objc_getMetaClass().

like image 178
jscs Avatar answered Oct 21 '22 13:10

jscs


+ (Class) classOf1 {
    return self ;
}

self in a class method is the Class object of the current class, so in here self is the class object for class Foo


+ (Class) classOf2 {
    return [Foo class] ;
}

and here is the class object for class Foo


+ (Class) classOf3 {
    return [[[Foo class] new] class] ;
}

this is same as

Class fooClass = [Foo class];
id fooInstance = [fooClass new];
return [fooInstance class];

which again, give you the class object of class Foo


+ (Class) classOf4 {
    return [[self new] class] ;
}

same as previous one, just replace [Foo class] with self


+ (Class) classOf5 {
    return [[[self alloc] init] class] ;
}

same as previous one, just replace [Foo new] with [[Foo alloc] init]


+ (Class) classOf6 {
    return [[[Foo alloc] init] class] ;
}

same as previous one, just replace self with Foo


+ (Class) classOf7 {
    return [self class] ;
}

same as second one, just replace Foo with self


The difference between using Foo and self in a class method is that, when this method is called on a subclass, self will be replaced to the subclass. e.g.

@interface Bar : Foo
@end

Class barClass = [Bar classOf1]; // same as [Bar class]
Class fooClass = [Bar classOf2]; // same as [Foo class]

If you really want to get the class of class (i.e. metaclass), you need to use objc runtime function from <objc/runtime.h>

id metaClass = objc_getMetaClass("Foo");
like image 40
Bryan Chen Avatar answered Oct 21 '22 12:10

Bryan Chen