In Objective-C, given an id, how can I tell what type of object it points to?

前端 未结 4 1237
走了就别回头了
走了就别回头了 2020-12-07 19:52

Objective-C newbie question. Given the following (fictional) code:

id mysteryObject = [anotherObject mysteriousMethod];

How can I determine

相关标签:
4条回答
  • 2020-12-07 20:27

    You can use isKindOfClass or isMemberOfClass

    For example:

    if ([foo isMemberOfClass:[NSBar class]])

    0 讨论(0)
  • 2020-12-07 20:29
    [mysteryObject class]
    

    will get you the class object. However, generally you want to do something OOPy like check for conformance to some protocol or interface.

    0 讨论(0)
  • 2020-12-07 20:32

    I found I had to cast back to id when used with a method defined in a @protocol.

    For example, self.listeners is an array of id

    If I do this ....

    for(id<PropertyListener> listener in self.listeners) {        
        if ( [ [ listener class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {
    

    I get an error "No known instance method for selector 'class'". Yet, when I cast the id from id to id it works ... Why I do not understand.

    [ ((id)listener) class] respondsToSelector .... 
    

    Here is the full loop ...

    for(id<PropertyListener> listener in self.listeners) {        
        if ( [ [ ((id)listener) class]  respondsToSelector:@selector(propertyChanged:propertyName:)]) {
            [listener propertyChanged: self propertyName:@"thePropName"];
        } else {
            [listener propertyChanged: self];
        }
    }
    
    0 讨论(0)
  • 2020-12-07 20:33

    In a dynamically typed language like Objective-C (or Python or Ruby), you often don't want to know what type of object it is. It's often more productive to think of whether the object responds to the message you wish to send; if it does, you shouldn't care what class it instantiates and if it doesn't you must handle the case regardless of the instance's type. This is known as "duck typing"...if it quacks like a duck it is a duck.

    You can test whether an object responds to a particular message (known as a selector in Objective-C) like this:

    if([mysteryInstance respondsToSelector:@selector(messageIWishToSend)]) {
      [mysteryInstance messageIWishToSend];
    } else {
      //handle case where instance doesn't respond to the desired message
    }
    

    Even better than testing for individual selectors is to define a @protocol that describes the API you wish to use for your classes:

    // MyProtocol.h
    @protocol MyProtocol
    - (void)methodInMyProtocol;
    @end
    
    //MyClass.h
    
    #import "MyProtocol.h"
    
    @interface MyClass <MyProtocol> {
    
    }
    - (void)methodInMyProtocol;
    @end
    

    You can test whether an instance implements the MyProtocol protocol like this:

    if([mysteryInstance conformsToProtocol:@protocol(MyProtocol)]) {
      [mysteryInstance methodInMyProtocol];
    } else {
      // ...
    }
    

    This way of doing things is often uncomfortable for folks coming from statically typed languages like Java or C++. You loose the compiler checking types for you. Dynamic typing makes a great many things easier however, including testing since you can easily replace an instance with a fake at test time. Thus the dynamic language approach is to test more and worry about types less. You do have good unit test coverage, don't you?

    If you really must determine the class of an instance at run time (and you really probably don't need to), you can use -[NSObject isKindOfClass:] to test whether an instance is an instance of a class or any of its subclasses or -[NSObject isMemberOfClass:] to test whether an instance is an instance of a particular class. You can inspect the Class object directly as the return of -[NSObject class] and you can get the string name of the instance's class with NSStringFromClass([mysteryInstance class]).

    0 讨论(0)
提交回复
热议问题