本帖最后由 楚燮哥哥 于 2014-12-1 15:03 编辑
最近在看mj老师讲的类的本质,自己也上网找了下资料,对mj老师讲的做一个总结和扩展。 我们都知道每个对象都是由类创建出来的,每个Object-C对象实例都是指向某块内存数据的指针,所以在声明变量时,类型后都需要有一个“*”字符,例如:- NSString *someString = @"someObject";
复制代码 该变量someString中存放的是这个字符串对象在内存中的地址,而NSString自身的数据就存放于该地址中,众所周知,对象所占的内存总是分配在“堆空间”(heap space)中,而指向该实例的变量总会被分配在“栈”(stack)上,他们此时在内存中的布局如下:
如果把对象所需的内存分配在栈上,则编译器会报错,例如
- NSString someString = @"someObject";
复制代码 会报如下错误 :Interface type cannot be statically allocated,翻译过来就是接口类型不能静态分配(本人只会谷歌翻译:#),可以理解为无法为该变量分配内存吧
对于通用的对象类型id,由于其本身已经是指针来,所以就无需跟上*字符,于是就有
- <font size="3">id someString = @"someObject";</font>
复制代码 这种写法虽然简洁,但由于没有在对象声明时指定具体类型,所以在其调用本身没有的方法时,编译器并不会报错。 描述Object-C对象所用的数据结构定义在运行期程序库的头文件里,id类型本身也是定义在这里:
- typeof struct obj_object{
- Class isa;
- } *id;
复制代码 由上可见,每个对象结构体的首个成员是Class类的变量。该变量定义了对象所属的类,通常称为“isa”指针,例如刚才的例子中所用的对象@"someObject"是一个(isa)NSString,isa指针就指向NSString,借此也可以说明类也是有类型的(Class isa这句代码本身就足以说明了),也就是说类本身也是一个对象,该对象的类型是Class,叫做类对象,而通过类创建的对象就叫类型的对象。例如有一个Person类,类中有一个类方法test(),那么就可以:
- [Person test];
- Class c =[[[Person alloc] init] class];
- [c test];
复制代码 大致也可以说Class是所有类的类型。
Class对象也定义在运行期程序库的头文件中:
- typedef struct objc_class *Class;
- struct objc_class {
- Class isa;
- Class super_class;
- const char *name;
- long version;
- long info;
- long instance_size;
- ……
- };
复制代码 此结构体中存放类的“元数据”(metadata),例如类的实例实现类几个方法,具备多少个实例变量等信息。结构体的首个变量也是isa指针,这说明Class本身也为Object-C对象。结构体里还有个变量叫super_class,它定义了本类的超类。类对象所属的类型(也就是isa指针所指向的类型)是另外一个类,叫做“元类”(metaclass),用来表述类对象本身所具备的元数据。前面我们一直所学的“类方法”就定义在此处,因为这些方法可以理解成类对象的实例方法。每个类仅有一个“类对象”,而每个“类对象”也仅有一个与之相关的“元类”。
假设有个名为someClass的子类从NSObject中继承而来(个人觉得NSObject类似于java中的final类),则其继承体系如下:
结构体中的super_class指针确立了类对象与元类之间的继承关系,而isa指针描述了实例所属的类。
以上这些就是所谓的“类继承体系”了。
至于类的加载无非就是程序启动时加载项目中的所有类,加载完毕后会只调用一次类的+load()方法,而当使用某个类时,就会调用该类的+initialize()方法为该类进行初始化,类加载和初始化的顺序为先父类后子类。
|