美文网首页
runtime_05

runtime_05

作者: 申申申申申 | 来源:发表于2018-04-06 18:20 被阅读29次

    首先感谢祖国,可以无忧无虑的码代码 ~


    If I have seen further, it is by standing on the shoudlers of giants.


    Objective-C Runtime Reference


    1. Category

    OC中的分类允许通过给一个类添加方法来进行扩充,并且不需要访问类中的代码就可以做到,但是通过分类不能添加新的实例变量
    Category是一个指向objc_category结构体的指针

    /// An opaque type that represents a category.
    typedef struct objc_category *Category;
    
    struct objc_category {
        // 分类名
        char * _Nonnull category_name                            OBJC2_UNAVAILABLE;
        // 分类所属类名
        char * _Nonnull class_name                               OBJC2_UNAVAILABLE;
        // 分类实例方法列表,是 objc_class 中方法列表的子集
        struct objc_method_list * _Nullable instance_methods     OBJC2_UNAVAILABLE;
        // 分类类方法列表,是元类的 方法列表的子集
        struct objc_method_list * _Nullable class_methods        OBJC2_UNAVAILABLE;
        // 分类所实现的协议列表
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    }                                                            OBJC2_UNAVAILABLE;
    

    <objc/runtime.h>没有关于category的操作函数,因为分类中的信息都包含在objc_class中,比如可以获取objc_class的方法列表来获取分类的方法,看栗子

    @implementation FFBaseViewController (statistics)
    - (void)baseVcCategoryMethod {
        NSLog(@"%s", __func__);
    }
    @end
    
    @implementation FFBaseViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        unsigned int outcount = 0;
        Method *methodList = class_copyMethodList(self.class, &outcount);
        for (int i = 0; i < outcount; i ++) {
            Method method = methodList[i];
            const char *name = sel_getName(method_getName(method));
            NSLog(@"FFBaseViewController ---> %s", name);
            if (strcmp(name, sel_getName(@selector(baseVcCategoryMethod))) == 0) {
                NSLog(@"FFBaseViewController 分类的方法在 objc_class 方法列表中 ---> %s", name);
            }
        }
    }
    
    @end
    // 2018-04-06 15:00:04.050 runtime[2810:172396] FFBaseViewController ---> baseVcCategoryMethod
    // 2018-04-06 15:00:04.050 runtime[2810:172396] FFBaseViewController 分类的方法在 objc_class 方法列表中 ---> baseVcCategoryMethod
    // 2018-04-06 15:00:04.050 runtime[2810:172396] FFBaseViewController ---> ff_viewWillAppear:
    // 2018-04-06 15:00:04.050 runtime[2810:172396] FFBaseViewController ---> baseVcShowSomething
    // 2018-04-06 15:00:04.051 runtime[2810:172396] FFBaseViewController ---> viewWillAppear:
    // 2018-04-06 15:00:04.051 runtime[2810:172396] FFBaseViewController ---> viewDidLoad
    
    2. Protocol

    OC中的协议是普遍存在的接口定义形式,在一个类中通过@protocol定义接口,在另外的类中实现接口,这种定义接口的形式也称作delegate模式@protocol声明了可以被其他任何类实现的方法,协议仅仅是定义了一个接口,而有其他去负责实现
    protocol是一个对象的结构体

    #ifdef __OBJC__
    @class Protocol;
    #else
    typedef struct objc_object Protocol;
    #endif
    
    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    

    运行时提供了一系列的函数,用来操作protocol

    • objc_getProtocol
      返回指定的协议,如果仅仅是声明了一个协议,而未在任何类中实现,返回nil
    /** 
     * Returns a specified protocol.
     * 
     * @param name The name of a protocol.
     * 
     * @return The protocol named \e name, or \c NULL if no protocol named \e name could be found.
     * 
     * @note This function acquires the runtime lock.
     */
    OBJC_EXPORT Protocol * _Nullable
    objc_getProtocol(const char * _Nonnull name)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • objc_copyProtocolList
      获取运行时所知道的所有协议的数组,使用后需要free()
    /** 
     * Returns an array of all the protocols known to the runtime.
     * 
     * @param outCount Upon return, contains the number of protocols in the returned array.
     * 
     * @return A C array of all the protocols known to the runtime. The array contains \c *outCount
     *  pointers followed by a \c NULL terminator. You must free the list with \c free().
     * 
     * @note This function acquires the runtime lock.
     */
    OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
    objc_copyProtocolList(unsigned int * _Nullable outCount)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_conformsToProtocol
      查看协议是否实现了另一协议
    /** 
     * Returns a Boolean value that indicates whether one protocol conforms to another protocol.
     * 
     * @param proto A protocol.
     * @param other A protocol.
     * 
     * @return \c YES if \e proto conforms to \e other, otherwise \c NO.
     * 
     * @note One protocol can incorporate other protocols using the same syntax 
     *  that classes use to adopt a protocol:
     *  \code
     *  @protocol ProtocolName < protocol list >
     *  \endcode
     *  All the protocols listed between angle brackets are considered part of the ProtocolName protocol.
     */
    OBJC_EXPORT BOOL
    protocol_conformsToProtocol(Protocol * _Nullable proto,
                                Protocol * _Nullable other)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_isEqual
      两个协议是否相等
    /** 
     * Returns a Boolean value that indicates whether two protocols are equal.
     * 
     * @param proto A protocol.
     * @param other A protocol.
     * 
     * @return \c YES if \e proto is the same as \e other, otherwise \c NO.
     */
    OBJC_EXPORT BOOL
    protocol_isEqual(Protocol * _Nullable proto, Protocol * _Nullable other)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_getName
      获取协议名称
    /** 
     * Returns the name of a protocol.
     * 
     * @param p A protocol.
     * 
     * @return The name of the protocol \e p as a C string.
     */
    OBJC_EXPORT const char * _Nonnull
    protocol_getName(Protocol * _Nonnull proto)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_getMethodDescription
      获取协议中指定的方法描述
    /** 
     * Returns a method description structure for a specified method of a given protocol.
     * 
     * @param p A protocol.
     * @param aSel A selector.
     * @param isRequiredMethod A Boolean value that indicates whether aSel is a required method.
     * @param isInstanceMethod A Boolean value that indicates whether aSel is an instance method.
     * 
     * @return An \c objc_method_description structure that describes the method specified by \e aSel,
     *  \e isRequiredMethod, and \e isInstanceMethod for the protocol \e p.
     *  If the protocol does not contain the specified method, returns an \c objc_method_description structure
     *  with the value \c {NULL, \c NULL}.
     * 
     * @note This function recursively searches any protocols that this protocol conforms to.
     */
    OBJC_EXPORT struct objc_method_description
    protocol_getMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull aSel,
                                  BOOL isRequiredMethod, BOOL isInstanceMethod)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_copyMethodDescriptionList
      获取协议中指定条件的方法的方法描述数组
    /** 
     * Returns an array of method descriptions of methods meeting a given specification for a given protocol.
     * 
     * @param p A protocol.
     * @param isRequiredMethod A Boolean value that indicates whether returned methods should
     *  be required methods (pass YES to specify required methods).
     * @param isInstanceMethod A Boolean value that indicates whether returned methods should
     *  be instance methods (pass YES to specify instance methods).
     * @param outCount Upon return, contains the number of method description structures in the returned array.
     * 
     * @return A C array of \c objc_method_description structures containing the names and types of \e p's methods 
     *  specified by \e isRequiredMethod and \e isInstanceMethod. The array contains \c *outCount pointers followed
     *  by a \c NULL terminator. You must free the list with \c free().
     *  If the protocol declares no methods that meet the specification, \c NULL is returned and \c *outCount is 0.
     * 
     * @note Methods in other protocols adopted by this protocol are not included.
     */
    OBJC_EXPORT struct objc_method_description * _Nullable
    protocol_copyMethodDescriptionList(Protocol * _Nonnull proto,
                                       BOOL isRequiredMethod,
                                       BOOL isInstanceMethod,
                                       unsigned int * _Nullable outCount)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_getProperty
      获取协议指定的属性
    /** 
     * Returns the specified property of a given protocol.
     * 
     * @param proto A protocol.
     * @param name The name of a property.
     * @param isRequiredProperty \c YES searches for a required property, \c NO searches for an optional property.
     * @param isInstanceProperty \c YES searches for an instance property, \c NO searches for a class property.
     * 
     * @return The property specified by \e name, \e isRequiredProperty, and \e isInstanceProperty for \e proto,
     *  or \c NULL if none of \e proto's properties meets the specification.
     */
    OBJC_EXPORT objc_property_t _Nullable
    protocol_getProperty(Protocol * _Nonnull proto,
                         const char * _Nonnull name,
                         BOOL isRequiredProperty, BOOL isInstanceProperty)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_copyPropertyList
      获取协议中的属性列表
    /** 
     * Returns an array of the required instance properties declared by a protocol.
     * 
     * @note Identical to 
     * \code
     * protocol_copyPropertyList2(proto, outCount, YES, YES);
     * \endcode
     */
    OBJC_EXPORT objc_property_t _Nonnull * _Nullable
    protocol_copyPropertyList(Protocol * _Nonnull proto,
                              unsigned int * _Nullable outCount)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • protocol_copyPropertyList2
    /** 
     * Returns an array of properties declared by a protocol.
     * 
     * @param proto A protocol.
     * @param outCount Upon return, contains the number of elements in the returned array.
     * @param isRequiredProperty \c YES returns required properties, \c NO returns optional properties.
     * @param isInstanceProperty \c YES returns instance properties, \c NO returns class properties.
     * 
     * @return A C array of pointers of type \c objc_property_t describing the properties declared by \e proto.
     *  Any properties declared by other protocols adopted by this protocol are not included. The array contains
     *  \c *outCount pointers followed by a \c NULL terminator. You must free the array with \c free().
     *  If the protocol declares no matching properties, \c NULL is returned and \c *outCount is \c 0.
     */
    OBJC_EXPORT objc_property_t _Nonnull * _Nullable
    protocol_copyPropertyList2(Protocol * _Nonnull proto,
                               unsigned int * _Nullable outCount,
                               BOOL isRequiredProperty, BOOL isInstanceProperty)
        OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0);
    
    • protocol_copyProtocolList
      获取协议采用的协议
    /** 
     * Returns an array of the protocols adopted by a protocol.
     * 
     * @param proto A protocol.
     * @param outCount Upon return, contains the number of elements in the returned array.
     * 
     * @return A C array of protocols adopted by \e proto. The array contains \e *outCount pointers
     *  followed by a \c NULL terminator. You must free the array with \c free().
     *  If the protocol declares no properties, \c NULL is returned and \c *outCount is \c 0.
     */
    OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
    protocol_copyProtocolList(Protocol * _Nonnull proto,
                              unsigned int * _Nullable outCount)
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • objc_allocateProtocol
      创建新的协议实例,如果已经存在同名协议返回nil
    /** 
     * Creates a new protocol instance that cannot be used until registered with
     * \c objc_registerProtocol()
     * 
     * @param name The name of the protocol to create.
     *
     * @return The Protocol instance on success, \c nil if a protocol
     *  with the same name already exists. 
     * @note There is no dispose method for this. 
     */
    OBJC_EXPORT Protocol * _Nullable
    objc_allocateProtocol(const char * _Nonnull name) 
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    • objc_registerProtocol
      在运行时注册新的协议,创建一个新的协议后,必须在运行时调用该函数进行注册,注册以后即可使用,但是不能再修改,即注册完成后不能再修改,即无法通过protocol_addMethodDescriptionprotocol_addProtocolprotocol_addProperty向协议中添加
    /** 
     * Registers a newly constructed protocol with the runtime. The protocol
     * will be ready for use and is immutable after this.
     * 
     * @param proto The protocol you want to register.
     */
    OBJC_EXPORT void
    objc_registerProtocol(Protocol * _Nonnull proto) 
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    • protocol_addMethodDescription
      为协议添加方法
    /** 
     * Adds a method to a protocol. The protocol must be under construction.
     * 
     * @param proto The protocol to add a method to.
     * @param name The name of the method to add.
     * @param types A C string that represents the method signature.
     * @param isRequiredMethod YES if the method is not an optional method.
     * @param isInstanceMethod YES if the method is an instance method. 
     */
    OBJC_EXPORT void
    protocol_addMethodDescription(Protocol * _Nonnull proto, SEL _Nonnull name,
                                  const char * _Nullable types,
                                  BOOL isRequiredMethod, BOOL isInstanceMethod) 
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    • protocol_addProtocol
      添加一个已经注册的协议到协议中
    /** 
     * Adds an incorporated protocol to another protocol. The protocol being
     * added to must still be under construction, while the additional protocol
     * must be already constructed.
     * 
     * @param proto The protocol you want to add to, it must be under construction.
     * @param addition The protocol you want to incorporate into \e proto, it must be registered.
     */
    OBJC_EXPORT void
    protocol_addProtocol(Protocol * _Nonnull proto, Protocol * _Nonnull addition) 
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    • protocol_addProperty
      为协议添加属性
    /** 
     * Adds a property to a protocol. The protocol must be under construction. 
     * 
     * @param proto The protocol to add a property to.
     * @param name The name of the property.
     * @param attributes An array of property attributes.
     * @param attributeCount The number of attributes in \e attributes.
     * @param isRequiredProperty YES if the property (accessor methods) is not optional. 
     * @param isInstanceProperty YES if the property (accessor methods) are instance methods. 
     *  This is the only case allowed fo a property, as a result, setting this to NO will 
     *  not add the property to the protocol at all. 
     */
    OBJC_EXPORT void
    protocol_addProperty(Protocol * _Nonnull proto, const char * _Nonnull name,
                         const objc_property_attribute_t * _Nullable attributes,
                         unsigned int attributeCount,
                         BOOL isRequiredProperty, BOOL isInstanceProperty)
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    3. super

    OC中,如果需要在类的方法中调用父类的方法的时候,通常会用到super,而superself不同,self是一个隐藏参数,每个方法实现的第一个参数就是self,而super并不是隐藏参数,而是一个“编译器标示符”,负责告诉编译器,在这里需要调用父类的方法,而不是本类中的方法,而实际上与self指向相同的消息接收者
    定义在<objc/message.h>

    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained _Nonnull id receiver;
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained _Nonnull Class class;
    #else
        __unsafe_unretained _Nonnull Class super_class;
    #endif
        /* super_class is the first class to search */
    };
    #endif
    

    这个结构体有两个成员

    • receiver 消息的实际接收者
    • super_class 指向当前类的父类
    /* Basic Messaging Primitives
     *
     * On some architectures, use objc_msgSend_stret for some struct return types.
     * On some architectures, use objc_msgSend_fpret for some float return types.
     * On some architectures, use objc_msgSend_fp2ret for some float return types.
     *
     * These functions must be cast to an appropriate function pointer type 
     * before being called. 
     */
    #if !OBJC_OLD_DISPATCH_PROTOTYPES
    OBJC_EXPORT void
    objc_msgSend(void /* id self, SEL op, ... */ )
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    
    OBJC_EXPORT void
    objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    #else
    /** 
     * Sends a message with a simple return value to an instance of a class.
     * 
     * @param self A pointer to the instance of the class that is to receive the message.
     * @param op The selector of the method that handles the message.
     * @param ... 
     *   A variable argument list containing the arguments to the method.
     * 
     * @return The return value of the method.
     * 
     * @note When it encounters a method call, the compiler generates a call to one of the
     *  functions \c objc_msgSend, \c objc_msgSend_stret, \c objc_msgSendSuper, or \c objc_msgSendSuper_stret.
     *  Messages sent to an object’s superclass (using the \c super keyword) are sent using \c objc_msgSendSuper; 
     *  other messages are sent using \c objc_msgSend. Methods that have data structures as return values
     *  are sent using \c objc_msgSendSuper_stret and \c objc_msgSend_stret.
     */
    OBJC_EXPORT id _Nullable
    objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    /** 
     * Sends a message with a simple return value to the superclass of an instance of a class.
     * 
     * @param super A pointer to an \c objc_super data structure. Pass values identifying the
     *  context the message was sent to, including the instance of the class that is to receive the
     *  message and the superclass at which to start searching for the method implementation.
     * @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
     * @param ...
     *   A variable argument list containing the arguments to the method.
     * 
     * @return The return value of the method identified by \e op.
     * 
     * @see objc_msgSend
     */
    OBJC_EXPORT id _Nullable
    objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
        OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
    #endif
    

    当使用super来接收消息的时候,编译器会生成objc_super结构体,这个结构体的receiver是当前类对象,与self一致,而super_class指向其父类
    而发送消息时候,不是调用的的objc_msgSend函数,而是调用的
    objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)该函数第一个参数即objc_super结构体,第二个参数是方法的SEL

    以在vc中的viewDidLoad中调用[super viewDidLoad]为例,
    函数objc_msgSendSuper的实际操作为,
    objc_super结构体指向的superClass的方法列表开始查找viewDidLoadselector
    找到后以objc->receiver去调用这个selector
    相当于objc_msgSend(objc_super->receiver, @selector(viewDidLoad))
    由于objc_super->receiverself本身,
    所以该方法实际与objc_msgSend(self, @selector(viewDidload))相同

    来验证

    @implementation FFBaseViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        NSLog(@"self.class ---> %@", self.class);
        NSLog(@"super.class ---> %@", super.class);
    }
    // 2018-04-06 16:36:48.460 runtime[3563:301512] self.class ---> FFBaseViewController
    // 2018-04-06 16:36:48.460 runtime[3563:301512] super.class ---> FFBaseViewController
    
    @end
    

    当使用[self class]时,这时的selfFFBaseViewController,在使用 objc_msgSend时,第一个参数是receiver也就是self,也是 FFBaseViewController *baseVC 这个实例。第二个参数,要先找到 class 这个方法的 selector,先从 FFBaseViewController 这个类开始找,没有,然后到 FFBaseViewController 的父类 UIViewController 中去找,也没有,等,一层一层向上找之后,在 NSObject 的类中发现这个 class 方法,而 NSObject 的这个class 方法,就是返回 receiver 的类别,所以这里输出 FFBaseViewController

    当使用 [super class] 时,这时要转换成 objc_msgSendSuper 的方法。先构造 objc_super 的结构体吧,第一个成员变量就是 self,第二个成员变量是 UIViewController,然后要找 class 这个 selector,先去 superClass 也就是 UIViewController 中去找,没有,然后去 UIViewController 的父类UIResponder中去找,结果还是在 NSObject 中找到了。然后内部使用函数 objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和用 [self class] 调用时相同了,此时的 receiver 还是 FFBaseViewController *baseVC,所以这里返回的也是 FFBaseViewController

    4. Working with Libraries

    库相关的操作,主要是用于获取系统提供的库的相关信息

    • objc_copyImageName
      获取所有加载的OC框架和动态库的名称
    /** 
     * Returns the names of all the loaded Objective-C frameworks and dynamic
     * libraries.
     * 
     * @param outCount The number of names returned.
     * 
     * @return An array of C strings of names. Must be free()'d by caller.
     */
    OBJC_EXPORT const char * _Nonnull * _Nonnull
    objc_copyImageNames(unsigned int * _Nullable outCount) 
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • class_getImageName
      获取指定类所在的动态库
    /** 
     * Returns the dynamic library name a class originated from.
     * 
     * @param cls The class you are inquiring about.
     * 
     * @return The name of the library containing this class.
     */
    OBJC_EXPORT const char * _Nullable
    class_getImageName(Class _Nullable cls) 
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    
    • objc_copyClassNamesForImage
      获取指定库或框架中的所有类的类名
    /** 
     * Returns the names of all the classes within a library.
     * 
     * @param image The library or framework you are inquiring about.
     * @param outCount The number of class names returned.
     * 
     * @return An array of C strings representing the class names.
     */
    OBJC_EXPORT const char * _Nonnull * _Nullable
    objc_copyClassNamesForImage(const char * _Nonnull image,
                                unsigned int * _Nullable outCount) 
        OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    

    eg:

    unsigned int outCount1 = 0;
    const char **names = objc_copyImageNames(&outCount1);
    for (int i = 0; i < outCount1; i ++) {
        NSLog(@"所有加载的框架或动态库的名称 -----> %s", names[i]);
    }
    free(names);
    // 所有加载的框架或动态库的名称 -----> /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 10.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/introspection/libdispatch.dylib
    // 所有加载的框架或动态库的名称 -----> /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 10.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libxpc.dylib
    // 所有加载的框架或动态库的名称 -----> /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 10.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/libsystem_trace.dylib
    // ...
    
    NSLog(@"获取指定类所在的动态库,UIView -----> %s", class_getImageName([UIView class]));
    // 获取指定类所在的动态库,UIView -----> /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 10.3.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/UIKit
    
    unsigned int outCount5 = 0;
    const char ** classNames = objc_copyClassNamesForImage(class_getImageName(NSClassFromString(@"UIView")), &outCount5);
    NSLog(@"outCount5 -----> %d", outCount5);
    for (int i = 0; i < outCount5; i ++) {
        NSLog(@"获取 uiview 中的所有类名 -----> %s", classNames[i]);
    }
    // outCount5 -----> 2079
    // 获取 uiview 中的所有类名 -----> UIGestureKeyboardIntroduction
    // 获取 uiview 中的所有类名 -----> _UIPreviewPresentationPlatterView
    // 获取 uiview 中的所有类名 -----> UIKeyboardUISettings
    // ...
    
    5. block

    运行时提供了一些函数支持针对block的操作

    • imp_implementationWithBlock
      创建一个指向函数的指针,当函数被调用时候,执行block中的操作
      参数block的签名必须是,method_return_type (^)(id self, method_args ...)形式,该方法能使用block作为IMP
    /** 
     * Creates a pointer to a function that will call the block
     * when the method is called.
     * 
     * @param block The block that implements this method. Its signature should
     *  be: method_return_type ^(id self, method_args...). 
     *  The selector is not available as a parameter to this block.
     *  The block is copied with \c Block_copy().
     * 
     * @return The IMP that calls this block. Must be disposed of with
     *  \c imp_removeBlock.
     */
    OBJC_EXPORT IMP _Nonnull
    imp_implementationWithBlock(id _Nonnull block)
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    

    eg:

    IMP blockImp = imp_implementationWithBlock(^(id obj, NSString *str) {
        NSLog(@"imp_implementationWithBlock -----> %@", str);
    });
    
    class_addMethod([FFWinterModel class], @selector(testBlock:), blockImp, "v@:@");
    [[FFWinterModel new] performSelector:@selector(testBlock:) withObject:@"imp_implementationWithBlock"];
    // imp_implementationWithBlock -----> imp_implementationWithBlock
    
    • imp_getBlock
      返回与使用imp_implementationWithBlock创建的IMP相关的block
    /** 
     * Return the block associated with an IMP that was created using
     * \c imp_implementationWithBlock.
     * 
     * @param anImp The IMP that calls this block.
     * 
     * @return The block called by \e anImp.
     */
    OBJC_EXPORT id _Nullable
    imp_getBlock(IMP _Nonnull anImp)
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    • imp_removeBlock
      解除block与使用imp_implementationWithBlock创建的IMP之前的关联关系,并释放blockcopy
    /** 
     * Disassociates a block from an IMP that was created using
     * \c imp_implementationWithBlock and releases the copy of the 
     * block that was created.
     * 
     * @param anImp An IMP that was created using \c imp_implementationWithBlock.
     * 
     * @return YES if the block was released successfully, NO otherwise. 
     *  (For example, the block might not have been used to create an IMP previously).
     */
    OBJC_EXPORT BOOL
    imp_removeBlock(IMP _Nonnull anImp)
        OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);
    
    6. weak
    • objc_loadWeak
      加载弱引用指针引用的对象,并返回
      该函数加载一个弱指针引用的对象,并在对其做retainautoreleasing操作后返回,这样对象就可以在调用者使用的时候保持足够长的生命周期
      该函数可在任何有使用__weak变量的表达式中使用
    /** 
     * This loads the object referenced by a weak pointer and returns it, after
     * retaining and autoreleasing the object to ensure that it stays alive
     * long enough for the caller to use it. This function would be used
     * anywhere a __weak variable is used in an expression.
     * 
     * @param location The weak pointer address
     * 
     * @return The object pointed to by \e location, or \c nil if \e *location is \c nil.
     */
    OBJC_EXPORT id _Nullable
    objc_loadWeak(id _Nullable * _Nonnull location)
        OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
    
    • objc_storeWeak
      存储__weak变量的新值,可用于__weak变量作为赋值对象时候
    /** 
     * This function stores a new value into a __weak variable. It would
     * be used anywhere a __weak variable is the target of an assignment.
     * 
     * @param location The address of the weak pointer itself
     * @param obj The new object this weak ptr should now point to
     * 
     * @return The value stored into \e location, i.e. \e obj
     */
    OBJC_EXPORT id _Nullable
    objc_storeWeak(id _Nullable * _Nonnull location, id _Nullable obj) 
        OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
    

    不定期更新 不合适的地方 还请指点~ 感激不尽
    愿祖国繁荣昌盛~
    o(* ̄3 ̄)o

    相关文章

      网友评论

          本文标题:runtime_05

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