美文网首页
iOS runtime

iOS runtime

作者: coder_my | 来源:发表于2019-10-11 22:06 被阅读0次

    参考:
    代码地址:https://opensource.apple.com/tarballs/objc4/
    文档地址:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1
    https://developer.apple.com/documentation/objectivec?language=objc

    Overview(概述)

    The Objective-C Runtime module APIs define the base of the Objective-C language. These APIs include:(Objective-C运行时模块api定义了Objective-C语言的基础。这些api包括:)

    • Types such as the NSObject class and the NSObject protocol that provide the root functionality of most Objective-C classes(类型,如NSObject类和NSObject协议,它们提供了大多数Objective-C类的根功能)
    • Functions and data structures that comprise the Objective-C runtime, which provides support for the dynamic properties of the Objective-C language(包含Objective-C运行时的函数和数据结构,它支持Objective-C语言的动态属性)
      You typically don't need to use this module directly.(通常不需要直接使用此模块。)

    objc_object

    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    

    objc_class

    typedef struct objc_class *Class;
    
    struct objc_class {
        //结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为类对象,类对象在编译期产生用于创建实例对象,是单例。
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE;
        const char * _Nonnull name                               OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
        struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    

    objc_method_list

    struct objc_method_list {
        struct objc_method_list * _Nullable 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;
    }   
    

    Method(objc_method)

    /// An opaque type that represents a method in a class definition.
    typedef struct objc_method *Method;
    struct objc_method {
        SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
        //方法的参数类型和返回值类型。
        char * _Nullable method_types                            OBJC2_UNAVAILABLE;
        //方法的实现,本质上是一个函数指针
        IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
    }         
    

    objc_cache

    //cache为方法调用的性能进行优化,通俗的讲,每当实例对象接收到一个消息时候,它不会直接在isa指向的类方法的方法列表中遍历查找能够响应消息的方法,效率太低了,而是有限在cache中查找。Runtime系统会把被调用的方法存在cache中,下次查找的时候效率更高。
    struct objc_cache {
        unsigned int mask /* total = mask + 1 */                 OBJC2_UNAVAILABLE;
        unsigned int occupied                                    OBJC2_UNAVAILABLE;
        Method _Nullable buckets[1]                              OBJC2_UNAVAILABLE;
    };
    

    SEL(objc_selector)

    /// An opaque type that represents a method selector.
    typedef struct objc_selector *SEL;
    

    objc_msgSend函数第二个参数类型为SEL,它是selector在Objective-C中的表示类型

    • 同一个类,selector不能重复
    • 不同的类,selector可以重复

    IMP

    /// A pointer to the function of a method implementation. 指向一个方法实现的指针
    typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
    

    Category(objc_category)

    //分类中可以添加实例方法,类方法,甚至可以实现协议,添加属性,不可以添加成员变量。
    struct category_t {
        //是指 class_name 而不是 category_name。
        const char *name;
        //要扩展的类对象,编译期间是不会定义的,而是在Runtime阶段通过name对 应到对应的类对象。
        classref_t cls;
        struct method_list_t *instanceMethods;
        //category中所有添加的类方法的列表。
        struct method_list_t *classMethods;
        //category实现的所有协议的列表。
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };
    

    Ivar(objc_ivar)

    typedef struct objc_ivar *Ivar;
    //表示类中实例变量的类型,objc_ivar可以根据实例查找其在类中的名字,也就是“反射”;
    struct objc_ivar {
        char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;
        char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;
        int ivar_offset                                          OBJC2_UNAVAILABLE;
    #ifdef __LP64__
        int space                                                OBJC2_UNAVAILABLE;
    #endif
    }    
    

    objc_property_t

    //@property标记了类中的属性,它是一个指向objc_property结构体的指针:
    typedef struct objc_property *objc_property_t;
    //通过class_copyPropertyList和protocol_copyPropertyList方法来获取类和协议中的属性
    OBJC_EXPORT objc_property_t _Nonnull * _Nullable
    class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
    OBJC_EXPORT objc_property_t _Nonnull * _Nullable
    protocol_copyPropertyList(Protocol * _Nonnull proto,
                              unsigned int * _Nullable outCount)
    unsigned int number = 0
    objc_property_t *propertys = class_copyPropertyList([self class], &number);
     for (int i = 0; i < number; i ++) {
           objc_property_t property = propertys[i];
           const char *name = property_getName(property);
           const char *type = property_getAttributes(property);
           NSString *nameStr = [NSString stringWithUTF8String:name];
           NSString *typeStr = [NSString stringWithUTF8String:type];
           NSLog(@"name === %@   type === %@", nameStr, typeStr);
     }
    

    objec_msgSend

    objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
    

    消息机制

    文档地址:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtHowMessagingWorks.html#//apple_ref/doc/uid/TP40008048-CH104-SW1

    The objc_msgSend Function

    In Objective-C, messages aren’t bound to method implementations until runtime. The compiler converts a message expression,(在Objective-C中,消息直到运行时才绑定到方法实现。编译器转换消息表达式,)

    [receiver message]

    into a call on a messaging function, objc_msgSend. This function takes the receiver and the name of the method mentioned in the message—that is, the method selector—as its two principal parameters:(调用消息传递函数objc_msgSend。该函数将接收方和消息中提到的方法名称(即方法选择器)作为其两个主要参数:)

    Any arguments passed in the message are also handed to objc_msgSend:(在消息中传递的任何参数也会传递给objc_msgSend:)

    objc_msgSend(receiver, selector, arg1, arg2, ...)

    The messaging function does everything necessary for dynamic binding:(消息传递函数执行动态绑定所需的所有操作:)

    • It first finds the procedure (method implementation) that the selector refers to. Since the same method can be implemented differently by separate classes, the precise procedure that it finds depends on the class of the receiver.(它首先找到选择器引用的过程(方法实现)。由于相同的方法可以由不同的类以不同的方式实现,所以它找到的精确过程取决于接收者的类。)

    • It then calls the procedure, passing it the receiving object (a pointer to its data), along with any arguments that were specified for the method.(然后调用该过程,将接收对象(指向其数据的指针)以及为该方法指定的任何参数传递给它。)

    • Finally, it passes on the return value of the procedure as its own return value.(最后,它将过程的返回值作为自己的返回值传递。)

    Note: The compiler generates calls to the messaging function. You should never call it directly in the code you write.(注意:编译器生成对消息传递函数的调用。在编写代码时,永远不要直接调用它)

    类和对象的结构.gif
    When a message is sent to an object, the messaging function follows the object’s isa pointer to the class structure where it looks up the method selector in the dispatch table. If it can’t find the selector there, objc_msgSend follows the pointer to the superclass and tries to find the selector in its dispatch table. Successive failures cause objc_msgSend to climb the class hierarchy until it reaches the NSObject class. Once it locates the selector, the function calls the method entered in the table and passes it the receiving object’s data structure.(当消息被发送到对象时,消息传递函数遵循对象的isa指针指向类结构,在这个类结构中,它在分派表中查找方法选择器。如果在那里找不到选择器,objc_msgSend会跟随指向超类的指针,并尝试在其分派表中找到选择器。连续的失败导致objc_msgSend爬上类层次结构,直到它到达NSObject类。一旦找到选择器,函数就调用表中输入的方法,并将接收对象的数据结构传递给它。)

    This is the way that method implementations are chosen at runtime—or, in the jargon of object-oriented programming, that methods are dynamically bound to messages.(这就是在运行时选择方法实现的方法—或者,用面向对象编程的术语来说,方法是动态绑定到消息的方法。)

    To speed the messaging process, the runtime system caches the selectors and addresses of methods as they are used. There’s a separate cache for each class, and it can contain selectors for inherited methods as well as for methods defined in the class. Before searching the dispatch tables, the messaging routine first checks the cache of the receiving object’s class (on the theory that a method that was used once may likely be used again). If the method selector is in the cache, messaging is only slightly slower than a function call. Once a program has been running long enough to “warm up” its caches, almost all the messages it sends find a cached method. Caches grow dynamically to accommodate new messages as the program runs.(为了加快消息传递过程,运行时系统在使用选择器和方法地址时缓存它们。每个类都有一个单独的缓存,它可以包含继承方法和类中定义的方法的选择器。在搜索分派表之前,消息传递例程首先检查接收对象的类的缓存(理论上,使用过一次的方法可能会再次使用)。如果方法选择器在缓存中,消息传递只比函数调用稍微慢一点。一旦一个程序运行了足够长的时间来“预热”它的缓存,它发送的几乎所有消息都会找到一个缓存的方法。缓存在程序运行时动态增长,以适应新的消息。)

    消息传递

    1. 系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
    2. 在它的类中查找method_list,是否有selector方法(先查缓存)。
    3. 没有则查找父类的method_list。
    4. 找到对应的method,执行它的IMP。
    5. 转发IMP的return值。

    实例、类、父类、元、根元类

    isa&superclass指向.jpg

    类对象就是一个结构体struct objc_class,这个结构体存放的数据称为元数据(metadata),
    类对象中的元数据存储的都是如何创建一个实例的相关信息,类对象和类方法就是从isa指针指向的结构体创建,类对象的isa指针指向的我们称之为元类(metaclass),
    struct objc_object结构体实例它的isa指针指向类对象,
    类对象的isa指针指向了元类,super_class指针指向了父类的类对象,
    而元类的super_class指针指向了父类的元类,那元类的isa指针又指向了自己。
    元类(Meta Class)是一个类对象的类。
    在上面我们提到,所有的类自身也是一个对象,我们可以向这个对象发送消息(即调用类方法)。
    为了调用类方法,这个类的isa指针必须指向一个包含这些类方法的一个objc_class结构体。这就引出了meta-class的概念,元类中保存了创建类对象以及类方法所需的所有信息。
    任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而根类的meta-class的isa指针是指向它自己。

    Using Hidden Arguments(两个隐藏参数)

    • The receiving object(接收的实例对象)
    • The selector for the method(方法选择器SEL)

    Dynamic Method Resolution(动态方法解析)
    文档地址:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtDynamicResolution.html#//apple_ref/doc/uid/TP40008048-CH102-SW1

    void dynamicMethodIMP(id self, SEL _cmd) {
        // implementation ....
    }
    @implementation MyClass
    + (BOOL)resolveInstanceMethod:(SEL)aSEL
    {
        if (aSEL == @selector(resolveThisMethodDynamically)) {
              class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
              return YES;
        }
        return [super resolveInstanceMethod:aSEL];
    }
    @end
    

    Message Forwarding(消息转发)
    文档地址:
    https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105-SW1

    消息转发.png
    1. 重定向,使用备用接收者
    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == @selector(foo)) {
            return [Person new];//返回Person对象,让Person对象接收这个消息
        }
        
        return [super forwardingTargetForSelector:aSelector];
    }
    
    1. 完整消息转发
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
            return [NSMethodSignature signatureWithObjCTypes:"v@:"];//签名,进入forwardInvocation
        }
        
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        SEL sel = anInvocation.selector;
    
        Person *p = [Person new];
        if([p respondsToSelector:sel]) {
            [anInvocation invokeWithTarget:p];
        }
        else {
            [self doesNotRecognizeSelector:sel];
        }
    
    }
    

    相关文章

      网友评论

          本文标题:iOS runtime

          本文链接:https://www.haomeiwen.com/subject/ccafyctx.html