NSObject中的isa到底是个什么?
首先看一下NSObject的定义:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
官方解释:Every object has an isa
instance variable that identifies the object's class. The runtime uses this pointer to determine the actual class of the object when it needs to.
Every object is connected to the run-time system through its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.
isa是一个Class, 那什么是Class? Class的定义:
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
哦, 是个结构体指针,objc_class是什么?
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif } OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
关键问题来了,objc_class里面也有一个Class isa,额,这是什么?下面我尽可能的简单的把事情说清楚。
1)首先很清楚这两个isa指向的不是同一个结构体:
2)究竟他们各自指向的是什么?先来看看第一个
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
这个isa指向的是class object,就是咱们经常说的类对象。不知道大家有没有印象,咱们经常会在一个类方法里面注册一个通知让这个“类”来监听一个某个事件,比如:
+ (void)registerEnterpriseVersionUpgrade
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(checkEnterpriseVersionAndTrigerUpgrade) name:UIApplicationWillEnterForegroundNotification object:nil];
}
这个类其实就是类对象,问题来了,这个“类”我没有创建过,它怎么会接收消息?是的,你没有创建过,但编译器帮你创建了,The class object is the compiled version of the class,而且是个单例。现在理解为什么一个“类”可以接收消息(类方法)了吧,因为他是一个被实例化的对象(后面会说到)。
这个类对象存储了struct objc_class里面的所有信息,方法名、属性、遵守的协议等等,额,等等,有个问题,方法名包括类方法和实例方法吗?等等,咱们看下实例化对象是怎么创建的。
当我们创建一个实例对象时,会这么操作:EnterpriseVersion *enterPriseVersion = [EnterpriseVersion new],咦,这是谁在接受alloc这个消息?想必你已经猜到了,就是我们前面说到的单例-类对象,类对象接收到new消息后(等会再说这个类对象是怎么知道可以接收这个消息的),根据自己存储的信息,创建了一个由enterPriseVersion指针指向的一个实例(NSObject)。每个实例都是继承自NSObject,自然也都有一个isa指针,这个isa指针都是指向这个单例,下面两个Class:aClass和bClass是一样的。
id aClass = [enterPriseVersion class];
id bClass = [EnterpriseVersion class];
创建完一个实例化对象enterPriseVersion之后,后面我们要做的就是向这个对象发送消息,比如我们向enterPriseVersion发送NSObject的一个实例化消息[enterPriseVersion copy],EnterpriseVersion并没有重写copy这个方法,它是如何找到父类(NSObject)的这个方法的呢?是这样的:
实例enterPriseVersion通过变量isa找到自己的类对象[EnterpriseVersion class](单例),前面说过,这个单例存储了实例的方法名、属性,遵守的协议等,单例查找到自己的方法列表是否有这个方法,如果有,则调用这个方法;如果没有,会根据保存的变量
Class super_class
找到父类对象[NSObject class](当然这也是个单例),然后在方法列表查找copy方法,找到后执行此消息,没有找到的话会进入异常处理(可以参考一些消息转发机制的文章)。
好了,到了这里实例化方法的调用很清楚了;那类方法的调用呢?这样聊到第二个isa了。
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif } OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
如同NSObject实例时通过isa指向的类对象(class object)创建的一样,类对象的也是通过自己的isa指向的元类(metaclass object)创建的,换句话说,类对象是元对象的实例!!!元类对象和类对象不同结构虽然相同,但存储的信息不同,元对象存储的是类对象的信息,如版本,名字,类方法等,而类对象存储的实例对象的信息,如变量名、实例方法等。
先来说说类方法是如何调用的,当执行了[EnterpriseVersion new]方法时,这个时候类对象收到个new消息(类方法)后,通过类对象的isa找到metaclass object(单例哦),在类方法列表查找是否有new方法,如果有,执行;如果没有,通过
Class super_class
查找到父metaclass object,同样在类方法列表查找new类方法,没有找到,继续通过父mataclass object的isa向上遍历...这样,类方法和类对象应该清楚了。
可问题来了,按照我们之前的逻辑metaclass object是个某个类的实例,它是谁创建的呢?这样类似的问题,算这次,问了3次了,不想再问了,也不想写了,可好消息是,这就结束了。
metaclass object是metaclass的实例,mataclass的通过super_class查找super metaclass,直到root metaclass,root metaclass的super_class指向自己。
总结一下:
instance object 的isa->class object, class object 的isa->metaclass object, metaclass object 的isa指root metaclass object(NSObject object), rootmetaclass object的isa指向自身;
注意:instance object只有isa这一个成员变量,没有super_class的哦...别混了, 查找super_class的是instance object的isa指向的class obcjet。
class_object的super_class->class_object的super class,然后继续super super class... 直到root class(NSOjbect class),root class 的super_class指向nil。
是不是清楚isa是什么了?下面来看一个具体的问题:
Objective-C的运行时动态特性决定了某个对象在生命周期内,其isa指向的类对象是可能改变的,也就是isa指针指向的对象有可能改变,Apple把这种技术叫做isa-swizzling。举个例子,当对某个对象使用了KVO之后,Objective-C实际上是动态的创建了另一个类对象,并把将isa指向这个实例。这时候该对象的isa指针就很不幸的被改变了。实际上,isa指针是Objective-C运行时系统使用到的一个变量,我们在程序中应该尽量不要依赖它。apple有云:
When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance. Instead of relying on the isa pointer your application should use the class method to determine the class of an object instance.
参考资料:
1)https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/ObjectAllocation/ObjectAllocation.html#//apple_ref/doc/uid/TP40010810-CH7-SW1
2)http://www.cocoadev.cn/CocoaDev/Key-Value-Observing-Quick-Start-cn.asp
3)https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/Introduction/Introduction.html#//apple_ref/doc/uid/TP40010810-CH1-SW1
4)http://blog.csdn.net/tskyfree/article/details/7984887
NSObject中的isa到底是个什么?的更多相关文章
- Python类中的self到底是干啥的
Python类中的self到底是干啥的 Python编写类的时候,每个函数参数第一个参数都是self,一开始我不管它到底是干嘛的,只知道必须要写上.后来对Python渐渐熟悉了一点,再回头看self的 ...
- 有趣的冷知识:编程中Foo, Bar 到底什么意思?
转自:编程中Foo, Bar 到底什么意思? 1 前言 在很多国外计算机书本和一些第三份开源软件的Demo中经常用到两个英文单词Foo,Bar.这到底是什么意思呢?从步入屌丝界的IT生活见到这两个单词 ...
- 【Java面试题】15 String s="Hello"; s=s+“world!”;这两行代码执行后,原始的String对象中的内容到底变了没有?String与StringBuffer的超详细讲解!!!!!
1.Java中哪些类是不能被继承的? 不能被继承的是那些用final关键字修饰的类.一般比较基本的类型或防止扩展类无意间破坏原来方法的实现的类型都应该是final的,在java中,System,Str ...
- NSObject 中执行Selector 的相关方法
1. 对当前Run Loop中Selector Sources的取消 NSObject中的performSelector:withObject:afterDelay:方法将会在当前线程的Run Loo ...
- iOS: NSObject中执行Selector的相关方法
本文转载至 http://www.mgenware.com/blog/?p=463 1. 对当前Run Loop中Selector Sources的取消 NSObject中的performSelect ...
- python中的cls到底指的是什么
python中的cls到底指的是什么,与self有什么区别? 2018年07月31日 11:13:09 rs勿忘初心 阅读数:7769 作者:秦风链接:https://www.zhihu.com/ ...
- linux中的selinux到底是什么
一文彻底明白linux中的selinux到底是什么 2018年06月29日 14:17:30 yanjun821126 阅读数 58877 标签: SElinux 更多 个人分类: Linux 一 ...
- Django中的request到底有啥属性
Django中的request到底有啥属性呢 Request 我们知道当URLconf文件匹配到用户输入的路径后,会调用对应的view函数,并将 HttpRequest对象 作为第一个参数传入该函 ...
- Java中的String到底占用多大的内存空间?你所了解的可能都是错误的!!
写在前面 最近小伙伴加群时,我总是问一个问题:Java中的String类占用多大的内存空间?很多小伙伴的回答着实让我哭笑不得,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道 ...
随机推荐
- http://www.cnblogs.com/eye-like/p/4121219.html
c# 操作Word总结 在医疗管理系统中为保存患者的体检和治疗记录,方便以后的医生或其他人查看.当把数据保存到数据库中,需要新建很多的字段,而且操作很繁琐,于是想到网页的信息创建到一个word文本中, ...
- Discuz!NT中集成Memcached分布式缓存
大约在两年前我写过一篇关于Discuz!NT缓存架构的文章,在那篇文章的结尾介绍了在IIS中如果开启多个应用程序池会造成多个缓存实例之间数据同步的问题.虽然给出了一个解决方案,但无形中却把压力转移到了 ...
- 使用Bugfree不应有的坏习惯
Bugfree是一款优秀的bug管理和追踪工具,因此受到不少公司的青睐.但实际的工作中,我发现不少开发或是测试的同事有一些不好的使用习惯,使得我们对Bugfree的利用不够高效.我下面列出使用Bugf ...
- python导入模块时的路径疑惑
有一个事儿,以前没注意,今天发现了,记录一下. 假设一个python文件a.py中,有一段代码,是打印当前路径的.当单独执行a.py文件的时候,打印的是a.py的位置. 但是当a.py文件被其他pyt ...
- 【CSS】Intermediate2:Pseudo Classes
1.specify a state or relation to the selector selector:pseudo_class { property: value; } 2.Link 3.Dy ...
- mini2440触摸屏驱动分析
mini2440驱动分析系列之 ---------------------------------------Mini2440触摸屏程序分析 By JeefJiang July,8th,2009 这是 ...
- 洛谷P1238 走迷宫
洛谷1238 走迷宫 题目描述 有一个m*n格的迷宫(表示有m行.n列),其中有可走的也有不可走的,如果用1表示可以走,0表示不可以走,文件读入这m*n个数据和起始点.结束点(起始点和结束点都是用两个 ...
- 洛谷P1120 小木棍
洛谷1120 小木棍 题目描述 乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过50. 现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长 ...
- Spark Pipe使用方法(外部程序调用方法)
写在前面: 1.我们使用的是Hadoop2.2.0,Spark 1.0. 2.这里使用的样例是经典的求pai程序来演示这个开发过程. 3.我们暂时使用java程序来开发,按照需要后面改用scala来开 ...
- HW4.44
public class Solution { public static void main(String[] args) { double randX; double randY; int hit ...