天天看點

runtime——Class——方法成員

SEL

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
           
OBJC_EXPORT const char *sel_getName(SEL sel)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT SEL sel_registerName(const char *str)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT SEL sel_getUid(const char *str)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT BOOL sel_isMapped(SEL sel)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT BOOL sel_isEqual(SEL lhs, SEL rhs) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
           

SEL應用

- (void)use_sel
{
    SEL sel1 = @selector(feedRice:andMeat:);
    SEL sel2 = @selector(feedRice:andMeat:);
    SEL sel3 = sel_registerName("feedRice:andMeat:");
    
    SEL sel4 = @selector(feedFruit:andFish:);
    SEL sel5 = @selector(feedFruit:andFish:);
    SEL sel6 = sel_registerName("feedFruit:andFish:");
    
    NSLog(@"sel1 = %p, sel2 = %p, sel3 = %p", sel1, sel2, sel3);
    NSLog(@"sel4 = %p, sel5 = %p, sel6 = %p", sel4, sel5, sel6);
    
    NSLog(@"%d, %d", sel1 == sel2, sel1 == sel4);
    NSLog(@"%d, %d", sel_isEqual(sel1, sel2), sel_isEqual(sel1, sel4));
    
    NSLog(@"%d, %d", sel_isMapped(sel1), sel_isMapped(sel4));
}
           

output:

sel1 = 0x1087314c7, sel2 = 0x1087314c7, sel3 = 0x1087314c7
sel4 = 0x1087314d9, sel5 = 0x1087314d9, sel6 = 0x1087314d9
1, 0
1, 0
1, 1
           

SEL總結

  • runtime system維護着SEL的dictionary,key為SEL對應字元串,value為SEL
  • @selector,sel_registerName,sel_getUid等同,根據字元串從SEL dictionary search SEL,如果found,傳回SEL,如果not found,建立後傳回
  • sel_isEqual同==
  • sel_isMapped判斷SEL是否存在SEL dictionary中

Method

/// Defines a method
struct objc_method_description {
	SEL name;               /**< The name of the method */
	char *types;            /**< The types of the method arguments */
};

struct objc_method {
    SEL method_name                                          OBJC2_UNAVAILABLE;
    char *method_types                                       OBJC2_UNAVAILABLE;
    IMP method_imp                                           OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id (*IMP)(id, SEL, ...); 
#endif

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
           
OBJC_EXPORT SEL method_getName(Method m) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP method_getImplementation(Method m) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT const char *method_getTypeEncoding(Method m) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT char *method_copyReturnType(Method m) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT char *method_copyArgumentType(Method m, unsigned int index) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_getReturnType(Method m, char *dst, size_t dst_len) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT unsigned int method_getNumberOfArguments(Method m)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT void method_getArgumentType(Method m, unsigned int index, 
                                        char *dst, size_t dst_len) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT struct objc_method_description *method_getDescription(Method m) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
           

Method應用

@interface FBMeat : NSObject

@end

@implementation FBMeat

@end

@interface FBAnimal : NSObject

- (void)feedRice:(int)rice andMeat:(FBMeat *)meat;

- (UIColor*)getColor:(int)cIndex;

@end

@implementation FBAnimal

- (void)feedRice:(int)rice andMeat:(FBMeat *)meat
{
}

- (UIColor*)getColor:(int)cIndex
{
    return [UIColor purpleColor];
}

@end
           
- (void)use_method
{
    {
        Method method = class_getInstanceMethod([FBAnimal class], @selector(feedRice:andMeat:));
        
        NSLog(@"-----feedRice:andMeat:-----");
        NSLog(@"type encoding = %s", method_getTypeEncoding(method));
        char* retType = method_copyReturnType(method);
        NSLog(@"ret type = %s", retType);
        free(retType);
        int argNum = method_getNumberOfArguments(method);
        NSLog(@"arg num = %d", argNum);
        for(int i = 0; i < argNum; ++i)
        {
            char *argType = method_copyArgumentType(method, i);
            NSLog(@"arg %d type = %s", i, argType);
            free(argType);
        }
    }
    
    {
        Method method = class_getInstanceMethod([FBAnimal class], @selector(getColor:));
        
        NSLog(@"-----getColor:-----");
        NSLog(@"type encoding = %s", method_getTypeEncoding(method));
        char* retType = method_copyReturnType(method);
        NSLog(@"ret type = %s", retType);
        free(retType);
        int argNum = method_getNumberOfArguments(method);
        NSLog(@"arg num = %d", argNum);
        for(int i = 0; i < argNum; ++i)
        {
            char *argType = method_copyArgumentType(method, i);
            NSLog(@"arg %d type = %s", i, argType);
            free(argType);
        }
    }
}
           

output:

-----feedRice:andMeat:-----
type encoding = [email protected]:[email protected]
ret type = v
arg num = 4
arg 0 type = @
arg 1 type = :
arg 2 type = i
arg 3 type = @
-----getColor:-----
type encoding = @[email protected]:8i16
ret type = @
arg num = 3
arg 0 type = @
arg 1 type = :
arg 2 type = i
           

Method總結

  • 類的方法(instance method和class method)類型本質為IMP類型,是以類的方法(instance method和class method)參數個數為IMP類型參數個數,即包含id和SEL

api

struct objc_method_list {
    struct objc_method_list *obsolete                        OBJC2_UNAVAILABLE;

    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;

struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
           
OBJC_EXPORT BOOL class_addMethod(Class cls, SEL name, IMP imp, 
                                 const char *types) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT Method class_getInstanceMethod(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT Method class_getClassMethod(Class cls, SEL name)
    __OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
OBJC_EXPORT Method *class_copyMethodList(Class cls, unsigned int *outCount) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_replaceMethod(Class cls, SEL name, IMP imp, 
                                    const char *types) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_getMethodImplementation(Class cls, SEL name) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
OBJC_EXPORT IMP class_getMethodImplementation_stret(Class cls, SEL name) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0)
     OBJC_ARM64_UNAVAILABLE;
OBJC_EXPORT BOOL class_respondsToSelector(Class cls, SEL sel) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
           

應用

@interface FBAnimal : NSObject

- (void)feedFood1:(int)food1 andFood2:(int)food2;
- (void)feedFood3:(int)food3 andFood4:(int)food4;

@end

@implementation FBAnimal

- (void)feedFood3:(int)food3 andFood4:(int)food4
{
}

- (void)feedFood1:(int)food1 andFood2:(int)food2
{
}

@end

@interface FBAnimal (FeedFood56)

- (void)feedFood3:(int)food3 andFood4:(int)food4;
- (void)feedFood5:(int)food5 andFood6:(int)food6;

@end

@implementation FBAnimal (FeedFood56)

- (void)feedFood3:(int)food3 andFood4:(int)food4;
{
}

- (void)feedFood5:(int)food5 andFood6:(int)food6
{
}

@end

@interface FBAnimal (FeedFood78)

- (void)feedFood3:(int)food3 andFood4:(int)food4;
- (void)feedFood7:(int)food7 andFood8:(int)food8;

@end

@implementation FBAnimal (FeedFood78)

- (void)feedFood3:(int)food3 andFood4:(int)food4;
{
}

- (void)feedFood7:(int)food7 andFood8:(int)food8
{
}

@end

@interface FBDog : FBAnimal

- (void)bark:(int)vol;
- (void)attack:(int)hurt;

@end

@implementation FBDog

- (void)bark:(int)vol
{
}

- (void)attack:(int)hurt
{
}

@end
           
- (void)class_methodLists
{
    Method method = class_getInstanceMethod([FBDog class], @selector(feedFood3:andFood4:));
    NSLog(@"method name = %s, method type = %s", sel_getName(method_getName(method)), method_getTypeEncoding(method));
    NSLog(@"respondsToSelector: %d", class_respondsToSelector([FBDog class], @selector(feedFood3:andFood4:)));
    
    NSLog(@"animal method list:");
    unsigned int animalMethodCnt = 0;
    Method* animalMethodList = class_copyMethodList([FBAnimal class], &animalMethodCnt);
    for(int i = 0; i < animalMethodCnt; ++i)
    {
        Method thisMethod = animalMethodList[i];
        NSLog(@"method name = %s, method type = %s", sel_getName(method_getName(thisMethod)), method_getTypeEncoding(thisMethod));
    }
    free(animalMethodList);
    
    NSLog(@"dog method list:");
    unsigned int dogMethodCnt = 0;
    Method* dogMethodList = class_copyMethodList([FBDog class], &dogMethodCnt);
    for(int i = 0; i < dogMethodCnt; ++i)
    {
        Method thisMethod = dogMethodList[i];
        NSLog(@"method name = %s, method type = %s", sel_getName(method_getName(thisMethod)), method_getTypeEncoding(thisMethod));
    }
    free(dogMethodList);
}
           

output:

method name = feedFood3:andFood4:, method type = [email protected]:8i16i20
respondsToSelector: 1
animal method list:
method name = feedFood3:andFood4:, method type = [email protected]:8i16i20
method name = feedFood3:andFood4:, method type = [email protected]:8i16i20
method name = feedFood3:andFood4:, method type = [email protected]:8i16i20
method name = feedFood1:andFood2:, method type = [email protected]:8i16i20
method name = feedFood5:andFood6:, method type = [email protected]:8i16i20
method name = feedFood7:andFood8:, method type = [email protected]:8i16i20
dog method list:
method name = bark:, method type = [email protected]:8i16
method name = attack:, method type = [email protected]:8i16
           

總結

  • 如果類中已含對應SEL的Method,class_addMethod不會添加到目前類中
  • 如果類中已含對應SEL的Method,class_replaceMethod等同于method_setImplementation,參數types ignored,如果類中不含對應SEL的Method,class_replaceMethod等同于class_addMethod
  • class_getInstanceMethod(class object)和class_getClassMethod(meta-class object)包含super_class
  • class_respondsToSelector包含super_class
  • class_copyIvarList不包含super_class
  • 繼承體系方法成員排列順序:父類->子類
  • 同一類方法成員排列順序:primary class implementation->category implementation
  • 同一類多個category方法成員排列順序:按category編譯順序排列,即先編譯category排前面,後編譯category排後面
  • 同一implementation(rimary class implementation,category implementation)方法成員排列順序:按方法成員編譯順序排列,即先編譯方法成員排前面,後編譯方法成員排後面
  • 如果primary class implementation與category implementation方法成員重複,這些重複方法成員排列在一起,順序按照primary class implementation->category implementation,如果多個category方法成員重複,按category編譯順序排列,即先編譯category排前面,後編譯category排後面

繼續閱讀