iOS 底层原理, 属性&方法分析

324 阅读4分钟

属性&成员变量&实例变量

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN
// 属性, 成员变量,实例变量
@interface Pet : NSObject
{   // 成员变量
    NSString *name;
    // 实例变量, 对象类型,有实例化的。可以添加属性的类型
    NSObject *objc;
}
// 属性, 下划线, getter, setter 方法
@property (nonatomic, copy) NSString *nickName;

@end

NS_ASSUME_NONNULL_END

属性 = 带下划线的成员变量 + setter + getter 方法 实例变量: 特殊的成员变量(类的实例化)

方法和属性分析

方法 : sel(方法编号) - imp(函数指针地址)

SEL 相当于书籍目录名称

IMP 相当于书本目录的页面

编译 main.m 文件

clang -rewrite-objc main.m -o main.cpp 把目标文件编译成c++文件
  • 这里我们查找我们的编译文件

方法归属分析

OC 方法主要有两种, 实例方法类方法

  • 经过探究,我们知道 实例方法->类中 类方法->元类中
1、如何获取类中的实例方法 2、如何获取元类中的类方法

1、如何获取类中的实例方法

首先打开苹果官方文档 developer.apple.com/documentati…

输入 runtime - 查找 Objective-C Functions

Function
class_copyMethodList(_:_:)
Describes the instance methods implemented by a class. // 描述由类实现的实例方法。

根据描述,我们创建 获取类的方法列表 的函数

void lgObjc_copyMethodList(Class pClass){
    unsigned int count = 0;
    Method *methods = class_copyMethodList(pClass, &count);
    for (unsigned int i=0; i < count; i++) {
        Method const method = methods[i];
        //获取方法名
        NSString *key = NSStringFromSelector(method_getName(method));
        
        LGLog(@"Method, name: %@", key);
    }
    free(methods);
}
  • 由当前文档 Discussion 我们看到
Discussion
To get the class methods of a class, use class_copyMethodList(object_getClass(cls), &count).

To get the implementations of methods that may be implemented by superclasses, use class_getInstanceMethod(_:_:) or class_getClassMethod(_:_:).

创建获取类的实例方法

void lgInstanceMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
    Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

    Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
    Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

创建 获取类的类方法 的函数

void lgClassMethod_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}

获取方法的具体实现: IMP(函数指针地址)

void lgIMP_classToMetaclass(Class pClass){
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);

    // - (void)sayHello;
    // + (void)sayHappy;
    IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
    IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));

    IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
    IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));

    NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
    NSLog(@"%s",__func__);
}

创建测试 对象, 类方法,实例方法

如果文档不清晰,这里我们可以配合 objc 源码 进行调试

  • 这里我们可以看到内部实现是获取元类的 实例方法
/***********************************************************************
* class_getClassMethod.  Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}
  • 测试如下:
// 获取类方法
void lgClassMethod_classToMetaclass(Class pClass){
    // 实例方法 存储在类中, 类方法,存储在元类中
    
    const char *className = class_getName(pClass);
    Class metaClass = objc_getMetaClass(className);
    
    Method method1 = class_getClassMethod(pClass, @selector(sayHello));
    Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
    // 首先判断, 是否是元类, 不是, 返回 元类, 然后在 元类中查找实例方法
    Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
    // 元类 为什么有 sayHappy 类方法 0 1
    //首先判断 元类 是否是元类,此时是,直接返回元类,然后在元类中查找 sayHappy实例方法,发现有这个实例方法,直接返回找到的实例方法
    Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
    
    LGLog(@"获取类方法: %s:\n method1:%p\n method2:%p \n method3:%p\n method4:%p",__func__,method1,method2,method3,method4);
    /*
     获取类方法: lgClassMethod_classToMetaclass:
     method1:0x0
     method2:0x0
     method3:0x100003148
     method4:0x100003148
     */
}

以上验证了我们的结论: 实例方法 存储在类中, 类方法,存储在元类中

类的判等

  • 我们知道如果要判断实例是否是某个类的子类,我们可以使用一下方法

iskindOfClassisMemberOfClass

iskindOfClass

  • Returns a Boolean value that indicates whether the receiver is an instance of given class or an instance of any class that inherits from that class.
  • 返回一个布尔值,该值指示接收方是给定类的实例还是从该类继承的任何类的实例。
void testKindOfClass() {
    /*
     // 查找流程: 元类 -> 根元类 -> 根类 -> nil
     + (BOOL)isKindOfClass:(Class)cls {
         // 类 vs 元类
         // 根元类 vs NSObject
         // NSObject vs NSObject
         // LGPerson vs 元类 (根元类) (NSObject)
         for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
             if (tcls == cls) return YES;
         }
         return NO;
     }
     
     // Calls [obj isKindOfClass]
     BOOL
     objc_opt_isKindOfClass(id obj, Class otherClass)
     {
     #if __OBJC2__
         if (slowpath(!obj)) return NO;
         Class cls = obj->getIsa();
         if (fastpath(!cls->hasCustomCore())) {
             for (Class tcls = cls; tcls; tcls = tcls->superclass) {
                 if (tcls == otherClass) {
                     return YES;
                 }
             }
             return NO;
         }
     #endif
         return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
     }
     
     // 查找流程: 对象的类 -> 父类 -> 根类 -> nil
     - (BOOL)isKindOfClass:(Class)cls {
         for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
             if (tcls == cls) return YES;
         }
         return NO;
     }
     */
    // 
    BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];       //
    BOOL re3 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];       //
    BOOL re4 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];       //

    NSLog(@"isKindOfClass:\n re1:%d\n re2:%d\n re3:%d\n re4:%d\n", re1, re2, re3, re4);
}

isMemberOfClass

  • Returns a Boolean value that indicates whether the receiver is an instance of a given class.
  • 返回一个布尔值,该值指示接收方是否是给定类的实例。
void testMemeberOfClass() {
    /*
     // 类的元素 与 传入类 对比
     + (BOOL)isMemberOfClass:(Class)cls {
         return self->ISA() == cls;
     }
     // 对象的父类 与 传入类 对比
     - (BOOL)isMemberOfClass:(Class)cls {
         return [self class] == cls;
     }
     */
    //
    BOOL re1 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];       //
    BOOL re2 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];       //
    BOOL re3 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];       //
    BOOL re4 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];       //

    NSLog(@"isMemberOfClass:\n re1:%d\n re2:%d\n re3:%d\n re4:%d\n", re1, re2, re3, re4);
}

isa 走位图

C 语言基础

#define LEN 20

struct names {
    char first[20];
    char last[20];
};

struct guy {
    struct names handle;
    char favfood[20];
    char job[20];
    float income;
};

// 用指针访问成员
+ (void)test2 {
    struct guy barney = {
        { "Ewen", "Villard" },
        "grilled salmon",
        "personality coach",
        .income = 68112.00
    };
    // 声明一个指向结构的指针
    struct guy * him;   /* 这是一个指向结构的指针 */
    // 如果barney是一个guy类型的结构,可以这样写
    him = &barney;
    // 换句话说,->运算符后面的结构指针和.运算符后面的结构名工作方式 相同(不能写成him.incone,因为him不是结构名)。
    NSLog(@"income:%f --- %f", him -> income, barney.income);
    /*
     这里要着重理解him是一个指针,但是hime->income是该指针所指向结 构的一个成员。
     所以在该例中,him->income是一个float类型的变量。
     */
    
    /*
     第2种方法是,以这样的顺序指定结构成员的值:
     如果him == &fellow[0],
     那么*him == fellow[0],
     因为&和*是一对互逆运算符。
     因此, 可以做以下替代:
     fellow[0].income == (*him).income 必须要使用圆括号,因为.运算符比*运算符的优先级高。
     总之,如果him是指向guy类型结构barney的指针,下面的关系恒成立:
     // 假设 him == &barney
     barney.income == (*him).income == him->income
     */
}