What is an @interface compiled into in Objective-C?
When I create an @interface
in Objective-C, such as:
@interface MyClass : NSObject {
@private
}
@end
It becomes 'something' when compiled. For instance I can do:
[MyClass class]
Or attach static methods, etc.
I am unable to do something 'meta' such as:
- (void)doSomethingWithInterfac开发者_如何学运维e:(id)myInterface
{
NSLog(@"myInterface = %@", myInterface);
Class aClass = [myInterface class];
}
I can pass a class around, but it would be cleaner if I could just pass the interface around. So...what does an interface become when compiled? Is there a doc that explains this somewhere? Can I do such 'meta' things with interfaces, or does the Class object of the interface essentially do all that?
Pretty sure you're confused about what an interface actually is in Objective-C. An interface is just the declaration of the data type and methods of a class. The Objective-C analog to Java interfaces is the protocol.
“Sure I could pass a class around…” You've just answered your own question. Because what an interface creates is its class! So
- (void)doSomethingWithClass:(Class)aClass {
NSLog(@"class == %@", aClass);
}
Pass something into that by using
[obj doSomethingWithClass:[obj class]];
If you really meant that you wanted to be passing around protocols, here's what you do. Assuming you have a protocol that looks like
@protocol Foldable <NSObject>
+ (id <Foldable>)unit;
- (id <Foldable>)append:(id <Foldable>)object;
@end
Then, you can do things with it like this:
- (void)doSomethingWithProtocol:(Protocol *)aProtocol {
NSLog(@"protocol == %s", protocol_getName(aProtocol));
}
You can pass a protocol into that method in the following way:
[obj doSomethingWithProtocol:@protocol(Foldable)];
As you can see, whereas you pass classes around by sending +class
to an Objective-C type identifier, you pass protocols around by using the @protocol()
directive.
Edit: More Information
To be clear, an interface is just an instruction to the compiler. You can't go and "grab" an interface and do something with it. But the interface causes a few things to happen:
- It causes a struct to be generated inside your compiled program; this struct has the instance variables of your class, as well as those of its superclasses.
- It causes the type name associated with that struct to be able to accept Objective-C messages (like
+class
or any other class method defined on it); it actually points to a metaclass object.
Now, the second point may be sort of confusing, but it's also pretty interesting. You've been told that classes are objects in Objective-C, which is mostly correct. But that might lead you to wonder why the following isn't possible:
[self doSomethingWithClass:NSObject];
The reason you can't do this is the same reason you couldn't expect something like the following to work:
[self doSomethingWithType:int];
Because it turns out that class identifiers (like NSObject
) are really just the struct type names, but with a twist: they can also receive messages. Hence, if you use an Objective-C class as a template parameter in C++, you could actually send messages to it!
template <typename T>
void something_crazy() {
NSLog(@"object: %@", [[T new] autorelease]);
}
something_crazy<NSArray>();
So that's a really long-winded way of answering your last question, what type does an interface become after compilation. The interface doesn't per se become anything or any time after compilation, but causes a special kind of pimped struct to be generated, which accepts messages acting as a metaclass.
An interface is the interface to a class. So it dictates many of the properties of the named class. Interfaces aren't separate entities; there's an explicit 1:1 mapping between interfaces and classes. So I'm unclear what you're trying to achieve. Can you explain why you think it would be cleaner to pass interfaces?
If you're looking for an abstraction that allows many classes to implement the same interface, then you're looking for protocols. The protocols declared as implemented by a class are part of its runtime information. And you can find out what methods are included in the protocol if you want. However, you can't go from a protocol to a class as you seem to wish as there isn't a 1:1 mapping.
EDIT: I think I understand you better now (apologies for my slow uptake). You're saying that given something like:
@interface SomeClass
{
// ...whatever...
}
// ... whatever...
@end
/* Elsewhere: */
- (void)doThingWithClass:(Class)class
{
// do something
}
You don't see why you have to use:
[instanceOfSomething doThingWithClass:[SomeClass class]];
Rather than:
[instanceOfSomething doThingWithClass:SomeClass];
The reason is that 'SomeClass' isn't an abstract symbol understood only by the compiler at compile time, it's an actual instance of a class (technically, it's a metaclass), present in memory at runtime. That's how the class methods work, e.g.
[SomeClass doSpecialThing];
Or:
[NSString stringWithString:@"someString"];
The compiler can't just guess that you mean the class, not the instance. So you use the 'Class' message to get the class from the instance. Possibly this explanation (Cocoa with Love's "What is a meta-class in Objective C?") will help.
Classes are objects, so you can say:
Class *someClass = [myObject class];
Now, someClass is a pointer to the class of which myObject is a member. You can write
[foo doSomethingWithClass:someClass];
and you're passing in the class itself -- the thing that's created when the @interface SomeClass : NSObject...
section is compiled.
As someone said previously, don't confuse Objective-C's @interface directive with Java interfaces. Objective-C's protocols are analogous to Java's interfaces; @interface is similar to Java's 'class' keyword.
精彩评论