记录的主要原因是在全网搜了很久,没有搜到什么靠谱的解读(为什么oc的指针对象的调用不需要用*号,这不是直接拿内存地址来玩吗?)
以下是比较通俗易懂的介绍
1.我们知道c语言中对于指针的操作是可以直接通过打印指针而得到对象的地址的,如下:
char *s = "abc";
printf("s的值:%s",s);
printf("s的地址:%p",s);
打印结果为:
s的值:abc
s的地址:0x107f3ced8
我们知道s其实是指向数据常量区“abc”的一个指针,我们可以通过*s来获取字符串首字符的值:
printf("首字符为:%c",*s);
打印结果为:
首字符为:a
2. 如此我们联想oc当中的字符串NSString,现在我们用oc的方式初始化一个字符串:
NSString *str = @"abc";
NSLog("str的值为:%@",str);
NSLog("str的地址为:%p",str);
打印结果如下:
str的值为:abc
str的地址为:0x1027510c8
3. 通过以上结果分析,我们在打印oc中字符串的值的时候为什么不能像c语言中直接使用指针*str,而是直接用%@打印str就获得了对象的值了呢,为什么不是这样的:
NSLog("str的值为:%@",*str);
其实NSString本身是一个对象,它不同于char *这些基本类型。本质上OC的对象是一个结构体。 NSLog在打印%@格式的对象时,会直接调用对象的description方法。与基本数据类型的处理是有区别的
例如下面的结构体:
typedef struct Object {
char *string;
} *Object;
打印的时候会进行如下:
Object obj = malloc(sizeof(Object));
obj->string = "Hello";
NSLog(@"%s", obj->string);
所以oc的%@也并不是意味着值的打印,它代表的是打印对象。
下面来简单说说指针对象调用方法为何不使用*,而是直接使用[指针对象 对象方法]的方式 :
这个是OC内部的机制,OC的对象指针指向一个对象,当你要调用对象方法时是先将方法名(SEL的数据类型)发送给指针指向的对象,对象通过内部方法列表的对应关系查找,找出对应方法的地址也就是类中定义的方法的地址,然后执行方法。
这一点可以仔细看看OC的消息机制
[指针对象 对象方法]
1、消息发送阶段
首先会判断消息接收者是否为空,如果为空,直接返回,如果不为空
通过isa指针找到类对象,从类对象的cache中查找,找到直接调用,找不到就从方法列表class_rw_t里面查找,找到直接调用并缓存到cache中,找不到就通过superclass指针找到父类的类对象,从父类类对象的cache中查找,找到直接调用并缓存到receiverClass的cache中,找不到就从父类类对象的方法列表class_rw_t里面查找,找到直接调用并缓存到receiverClass的cache中,找不到就继续通过superclass指针往上找,直到基类都找不到的话就进入动态方法解析阶段2、动态方法解析阶段
首先会判断是否已进行过动态方法解析
否:会调用resolveInstanceMethod:方法来动态解析方法,并且标记为已经动态解析
是:进入消息转发阶段3、消息转发阶段
调用forwardingTargetForSelector:方法看返回值是否为nil
否:objc_msgSend(返回值,SEL)
是:调用methodSignatureForSelector:方法进行方法签名
看返回值是否为nil
否:调用forwardInvocation:方法
是:调用doesNotRecognizeSelector:方法
objc_msgSend如果找不到合适的方法进行调用,会报错 unrecognized selector sent to instance
先到这里吧,有疑惑的同学可以调试下代码看看内部运作方式。