OC基础-类的本质+description+SEL

时间:2022-08-24 14:57:50
<7>类的本质
1. 类本身也是对象,是个Class类型的对象,简称类对象;而类(类对象)所创建的对象叫做实例对象,它所属的类型是创建它的那个类(注意的是:Class本身带有 * )

2. 内存中每个类只加载一次(一份)
    Class c = [p class];    // p为Person类的一个对象  此时c是Person类的地址(返回一个类的地址,所以c也可以创建一个Person类的对象),这叫做获取内存中的类对象
    Class c1 = [p1 class];   // p1为Person类的一个对象  此时c1是Person类的地址(返回一个类的地址),这叫做获取内存中的类对象
    所以 c = c1
    获取内存中类的另一种方法
    Class c2 = [Person class];   //c=c1=c2
    输出地址用%p

3. 类对象就是类就是类名

4. + (void)load方法:当程序启动的时候,就会加载一次项目中所有的类和分类,类加载完毕后就会调用+load方法,每个类调用一遍但是只会调用一次(不管你用没用到这个类都会调用这个方法)
   + (void)initialize方法:当第一次使用这个类的时候,就会调用+initialize方法(只有用到这个类,才会调用这个方法)

    先加载父类,再加载子类(先调用父类的+load方法,在调用子类的+load方法)
    先初始化父类,再初始化子类(先调用父类的+initialize方法,在调用子类的+initialize方法)(利用这个方法可以监听这个类在什么时候被使用了,也可以通过重写该类的init方法来让这个类在第一次使用时初始化我们想做的一些事)


<8>description方法
1. 打印对象用%@

2. 打印对象中的所有属性值(描述这个对象)

3. -description方法(不在NSObject类中):

    Person *p = [[Person alloc] init];
    NSLog(@“%@”, p);

    默认情况下,用NSLog和%@‘直接’输出对象时,结果是:<类名:内存地址>:实际上分为下面的三步

    1>当使用NSLog函数和%@输出对象时,会首先调用对象p的-description方法(-description方法的返回值是NSString *)
    2>拿到-description方法的返回值(NSString *)显示到屏幕上
    3>-description方法默认返回的是“类名+内存地址”

    可以通过重写-description这个方法来打印p对象
    - (NSString *)description
    {
        return [NSString stringWithFormat:@“age=%d,name=%@”, _age, _name]; //返回一个由对象属性值组成的字符串
    }
    这时再NSLog(@“%@”, p); 就是p对象的属性值了(描述p)

    还有要注意避免引发死循环!(不要在重写的-description方法中输出一个self或者说一个对象)

    -description方法决定了实例对象的输出结果
    

4. +description方法(在NSObject类中)
    Class c = [Person class];
    NSLog(@“%@”, c);
    1>会调用类的+description方法
    2>拿到+description方法的返回值(NSString *)显示到屏幕上(结果:该类的类名)
    同样可以重写+description方法来打印类对象
    +description方法决定了类对象的输出结果

5. 输出补充
    NSLog(@“%p”, p);  =   NSLog(@“%@”, p);   //都是p的地址,不重写-description方法的前提下
    查看NSLog的文档  _LINE_  _FILE_  _func_


<9>SEL类型
1. SEL类型的变量里存的是类的方法的地址
    1>把方法包装成SEL类型的数据
    2>根据SEL数据找到对应的方法地址
    3>根据方法地址调用对应的方法
    
    4>第一次包装的时候是比较耗性能的,他得一个一个找/匹配,但是做完后就会有一个缓存,以后就不用做了,直接查找缓存

2. 调用方法的两种方式(直接的 间接的)
    不带参数的
    直接:[p test];
    间接:[p performSelector:@selector(test)];

    带参数的
    [p test3:@“abc”];
    [p performSelector:@selector(test3:) withObject:@“abc”];  // : 也是方法名的一部分

    发消息就是发SEL数据(消息机制)

3. SEL本身也有 *

4. 每个类的方法列表都存储在类对象中,每个方法都有一个与之对应的SEL数据对象,根据一个SEL对象就可以找到方法的地址,进而调用该方法

5. SEL对象的创建
    1> SEL s1 = @selector(test:);   // 这里是带参数的方法test,所以有冒号,: 也是方法名的一部分

    2> 知道类中有一个方法名叫‘test’ ,但是这个方法名是一个字符串,我们知道一个方法名的字符串形式,怎么调用该方法?
       SEL s2 = NSSelectorFromString(@“test”);     // 1.先把字符串形式的方法名转换成SEL数据
       [p performSelector:s2];    // 2.再调用该方法的SEL数据(传给performSelector方法)

       例子:
       Person *p = [[Person alloc] init];
       NSString *name = @“test”;
       SEL s2 = NSSelectorFromString(name);
       [p performSelector:s2];

6. 每个方法中都有一个隐藏的 _cmd SEL数据,它指向当前的方法(指向本身),要想输出 _cmd,要先把_cmd转换成字符串,然后再输出,不能直接输出(有相应的方法),不能在方法中用_cmd, 否则会引发死循环

7. 学到运行机制时,就可以使用这个SEL直接拿到方法地址调用方法,如果一个方法使用非常频繁,包装比较麻烦,会耗费性能,所以我们可以直接拿到方法的SEL去调用方法,降低程序运行的消耗,提高性能