• OC基础(10)


    id类型

    本小节知识:

    1. 【理解】静态类型和动态类型
    2. 【理解】为什么要有动态类型?
    3. 【理解】id数据类型与静态类型

    1.静态类型和动态类型

    • 静态类型
      • 将一个指针变量定义为特定类的对象时,使用的是静态类型,在编译的时候就知道这个指针变量所属的类,这个变量总是存储特定类的对象。
    Person *p = [Person new];
    • 动态类型
      • 这一特性是程序直到执行时才确定对象所属的类
    id obj = [Person new];

    2.为什么要有动态类型?

    • 我们知道NSObject是OC中的基类
    • 那么任何对象的NSObject类型的指针可以指向任意对象,都没有问题
    • 但是NSObject是静态类型,如果通过它直接调用NSObject上面不存在的方法,编译器会报错。
    • 你如果想通过NSObject的指针调用特定对象的方法,就必须把NSObject * 这种类型强转成特定类型。然后调用。如下
    //定义NSObject * 类型
     NSObject* obj = [Cat new];
     Cat *c = (Cat*)obj;
     [c eat];
    • id 是一种通用的对象类型,它可以指向属于任何类的对象,也可以理解为万能指针 ,相当于C语言的 void *
    • 因为id是动态类型,所以可以通过id类型直接调用指向对象中的方法, 编译器不会报错
    /// Represents an instance of a class.
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
     id obj = [C at new];
     [obj eat]; // 不用强制类型转换
    
     [obj test]; //可以调用私有方法
    • 注意:
      • 在id的定义中,已经包好了*号。id指针只能指向OC中的对象
      • 为了尽可能的减少编程中出错,Xcode做了一个检查,当使用id 类型的调用本项目中所有类中都没有的方法,编译器会报错
      • id类型不能使用.语法, 因为.语法是编译器特性, 而id是运行时特性

    3.id数据类型与静态类型

    • 虽然说id数据类型可以存储任何类型的对象,但是不要养成滥用这种通用类型

      • 如没有使用到多态尽量使用静态类型
      • 静态类型可以更早的发现错误(在编译阶段而不是运行阶段)
      • 静态类型能够提高程序的可读性
      • 使用动态类型前最好判断其真实类型
    • 动态类型判断类型

      • - (BOOL)isKindOfClass:classObj 判断实例对象是否是这个类或者这个类的子类的实例
        Person *p = [Person new];
        Student *stu = [Student new];
    
        BOOL res = [p isKindOfClass:[Person class]];
        NSLog(@"res = %i", res); // YES
        res = [stu isKindOfClass:[Person class]];
        NSLog(@"res = %i", res); // YES
    - (BOOL) isMemberOfClass: classObj 判断是否是这个类的实例
        Person *p = [Person new];
        Student *stu = [Student new];
    
        BOOL res = [p isMemberOfClass:[Person class]];
        NSLog(@"res = %i", res); // YES
        res = [stu isMemberOfClass:[Person class]];
        NSLog(@"res = %i", res); // NO
    + (BOOL) isSubclassOfClass:classObj 判断类是否是指定类的子类
        BOOL res = [Person isSubclassOfClass:[Student class]];
        NSLog(@"res = %i", res); // NO
    
        res = [Student isSubclassOfClass:[Person class]];
        NSLog(@"res = %i", res); // YES

    SEL类型

    本小节知识:

    1. 【理解】什么是SEL类型
    2. 【掌握】SEL使用
    3. 【理解】OC方法查找顺序

    1.什么是SEL类型

    • SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系

    • 每个类的方法列表都存储在类对象中

    • 每个方法都有一个与之对应的SEL类型的对象

    • 根据一个SEL对象就可以找到方法的地址,进而调用方法

    • SEL类型的定义

      • typedef struct objc_selector *SEL;
    • 首先把test这个方法名包装成sel类型的数据

    • 根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码

    • 如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码

    • 如果没有找到,一直像上找,直到基类(NSObject)

    • 如果都没有找到就报错。

    • 注意: + 在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。

    Dog *dog=[[Dog alloc] init];
    [dog eat];

    2.SEL使用

    • 定义普通的变量

      • 如:SEL sel = @selector(show);
    • 作为方法实参与NSObject配合使用

    • 检验对象是否实现了某个方法

      • - (BOOL) respondsToSelector: (SEL)selector 判断实例是否实现这样方法
      • + (BOOL)instancesRespondToSelector:(SEL)aSelector;
        BOOL flag;
        // [类 respondsToSelector]用于判断是否包含某个类方法
        flag = [Person respondsToSelector:@selector(objectFun)]; //NO
        flag = [Person respondsToSelector:@selector(classFun)]; //YES
    
        Person *obj = [[Person alloc] init];
    
        // [对象 respondsToSelector]用于判断是否包含某个对象方法
        flag = [obj respondsToSelector:@selector(objectFun)]; //YES
        flag = [obj respondsToSelector:@selector(classFun)]; //NO
    
        // [类名 instancesRespondToSelector]用于判断是否包含某个对象方法
        // instancesRespondToSelectorr只能写在类名后面, 等价于 [对象 respondsToSelector]
        flag = [Person instancesRespondToSelector:@selector(objectFun)]; //YES
        flag = [Person instancesRespondToSelector:@selector(classFun)]; //NO
    • 让对象执行某个方法
      • - (id)performSelector:(SEL)aSelector;
      • - (id)performSelector:(SEL)aSelector withObject:(id)object;
      • - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
        Person *p = [Person new];
        SEL s1 = @selector(objectFun);
        [p performSelector:s1];
    
        SEL s2 = @selector(objectFun:);
        [p performSelector:s2 withObject:@"lnj"];
    
        SEL s3 = @selector(objectFun:value2:);
        [p performSelector:s3 withObject:@"lnj" withObject:@"lmj"];
    
        SEL s4 = @selector(classFun);
        [Person performSelector:s4];
    
        SEL s5 = @selector(classFun:);
        [Person performSelector:s5 withObject:@"lnj"];
    
        SEL s6 = @selector(classFun:value2:);
        [Person performSelector:s6 withObject:@"lnj" withObject:@"lmj"];
    • 作为方法形参
    @implementation Person
    
    - (void)makeObject:(id) obj performSelector:(SEL) selector
    {
        [obj performSelector:selector];
    }
    @end
    
    int main(int argc, const char * argv[]) {
    
        Person *p = [Person new];
        SEL s1 = @selector(eat);
        Dog *d = [Dog new];
        [p makeObject:d performSelector:s1];
    
        return 0;
    }

    3.OC方法查找顺序

     - 1.给实例对象发送消息的过程(调用对象方法) + 根据对象的isA指针去该对象的类方法中查找,如果找到了就执行 + 如果没有找到,就去该类的父类类对象中查找 + 如果没有找到就一直往上找,直到跟类(NSObject) + 如果都没有找到就报错

    • 2.给类对象发送消息(调用类方法)
      • 根据类对象的isA指针去元对象中查找,如果找到了就执行
      • 如果没有找到就去父元对象中查找
      • 如果如果没有找到就一直往上查找,直到根类(NSOject)
      • 如果都没有找到就报错

  • 相关阅读:
    java程序调用CMD命令启动tomcat替换环境变量
    解决mysql中只能通过localhost访问不能通过ip访问的问题
    mysql 主从配置
    maven私服上传jar包
    mysql 服务【安装】【启动】【停止】【卸载】【重置密码】
    spring boot 文件上传大小限制
    Mysql 字符串分隔函数
    上取整和下取整之间的转换关系
    chapter3 数据链路层
    Chapter2 物理层
  • 原文地址:https://www.cnblogs.com/zhoudaquan/p/5015775.html
Copyright © 2020-2023  润新知