比较简单的一篇英文,重点是讲解meta-class。翻译下,加深理解。

原文标题:What is a meta-class in Objective-C?

原文地址:http://www.cocoawithlove.com/2010/01/what-is-meta-class-in-objective-c.html

本篇将会探讨一个在Objective-C中相对陌生的概念 -- meta-class。OC中的每一个类都会有一个与之相关联的meta class,但是你却几乎永远也不会直接使用到,它们始终笼罩着一层神秘的面纱。笔者将以运行时动态创建一个class为引,通过剖析创建的class pair来弄明白到底meta-class是什么以及更深入的了解它对于OC中对象、类的意义。



在运行时创建类

以下代码演示运行时创建一个NSError的子类,同时添加一个实例方法给它:

  1. Class newClass =
  2. objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
  3. class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
  4. objc_registerClassPair(newClass);

函数ReportFunction就是添加的实例方法的具体实现,如下:

  1. void ReportFunction(id self, SEL _cmd)
  2. {
  3. NSLog(@"This object is %p.",self);
  4. NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]);
  5. Class currentClass = [self class];
  6. for( int i = 1; i < 5; ++i )
  7. {
  8. NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
  9. currentClass = object_getClass(currentClass);
  10. }
  11. NSLog(@"NSObject's class is %p", [NSObject class]);
  12. NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class]));
  13. }

看起来一切都很简单,运行时创建类只需要三步:

1、为"class pair"分配空间(使用objc_allocateClassPair).

2、为创建的类添加方法和成员(上例使用class_addMethod添加了一个方法)。

3、注册你创建的这个类,使其可用(使用objc_registerClassPair)。

估计读者马上就要问:什么是“class pair"? objc_allocateClassPair只返回一个值:Class。那么pair的另一半在哪里呢?

是的,估计你已经猜到了这个另一半就是meta-class,也就是这篇短文的标题,但是要解释清楚它是什么,为什么需要它,还需要交代下OC的对象与类的相关背景。

一个数据结构何以成为一个对象?



每个对象都会有一个它所属的类。这是面向对象的基本概念,但是在OC中,这对所有数据结构有效。任何数据结构,只要在恰当的位置具有一个指针指向一个class,那么,它都可以被认为是一个对象。

在OC中,一个对象所属于哪个类,是由它的isa指针指向的。这个isa指针指向这个对象所属的class。

实际上,OC中对象的定义是如下的样子:

  1. typedef struct objc_object {
  2. Class isa;
  3. }*id;

这个定义表明:任何以一个指向Class的指针作为首个成员的数据结构都可以被认为是一个objc_object.

最重要的特性就是,你可以向OC中的任何对象发送消息,如下这样:

  1. 【@”stringValue" writeToFile:@"/file.txt atomically:YES encoding: NSUTF8StringEncoding error:NULL];

运行原理就是,当你向一个OC对象发送消息时(上文的@“stringValue”),运行时库会根据对象的isa指针找到这个对象所属的类(上文为例,会找到NSCFString类).这个类会包含一个所有实例方法的列表及一个指向superclass的指针以便可以找到父类的实例方法。运行时库会在类的方法列表以及父类(们)的方法列表中寻找符合这个selector(上文为例,这个selector是"writeToFile:atomically:encoding:error")的方法。找到后即运行这个方法。关键点就是类要定义这个你发送给对象的消息。

什么是meta-class?



至此,你可能已经知道,一个OC的类其实也是一个对象,意思就是你可以向一个类发送消息。

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];

在这个例子中,defaultStringEncoding 被发送给了NSString类。因为每一个OC的类本身也是一个对象。也就是说Class的数据结构必然也是以isa指针开始的在二进制级别上与objc_object是完全兼容的。然后一个类结构的下一个字段一定是一个指向super class的指针(或者指向nil,对于基类而言)。

一个类如何定义有很多方法,依赖于你的运行时库版本,但是不管哪种方法,他们都是以一个isa作为第一个字段,接着是superclass字段。

  1. typedef struct objc_class *Class;
  2. struct objc_class{
  3. Class isa;
  4. Class super_class;
  5. /*followed by runtime specific details...*/
  6. };

为了可以调用类方法,这个类的isa指针必须指向一个包含这些类方法的类结构体。

这样就引出了meta-class的概念:meta-class是一个类对象的类。

简单解释下:

       当你向一个对象发送消息时,runtime会在这个对象所属的那个类的方法列表中查找。

       当你向一个类发送消息时,runtime会在这个类的meta-class的方法列表中查找。

meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。

meta-class的类又是什么呢?



meta-class,就像Class一样,也是一个对象。你依旧可以向它发送消息调用函数,自然的,meta-class也会有一个isa指针指向其所属类。所有的meta-class使用基类的meta-class作为他们的所属类。具体而言,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己所属的类。

根据这个规则,所有的meta-class使用基类的meta-class作为它们的类,而基类的meta-class也是属于它自己,也就是说基类的meta-class的isa指针指向它自己。(译:完美的闭环)

类和meta-class的继承



就像一个类使用super_class指针指向自己的父类一样,meta-class的super_class会指向类的super_class的meta-class。一直追溯到基类的meta-class,它的super_class会指向基类自身。(译:万物归根)

这样一来,整个继承体系中的实例、类和meta-class都派生自继承体系中的基类。对于NSObject继承体系来说,NSObject的实例方法对体系中所有的实例、类和meta-class都是有效的;NSObject的类方法对于体系中所有的类和meta-class都是有效的。

用文字描述总会让人迷糊,Greg Parker给出了一份精彩的图谱来展示这些关系:

http://www.sealiesoftware.com/blog/class%20diagram.pdf

实验证明:



为了证实以上的论述,让我们查看下开篇代码中ReportFunction的输出。这个函数的目的就是沿着isa指针进行打印。

为了运行ErportFunction,我们需要创建一个实例,并调用report方法。

  1. id instanceOfNewClass = [[newClass alloc]initWithDomain:@"some Domain" code:0 userInfo:nil];
  2. [instanceOfNewClass performSelector:@"report)];
  3. [instanceOfNewClass release];

因为我们并没有对report方法进行声明,所以我们使用performSelector进行调用,这样避免编译器警告。

然后ReportFunction函数会沿着isa进行检索,来告诉我们class,meta-class以及meta-class的class是什么样的情况:

【注:ReportFunction使用object_getClass来获取isa指针指向的类,因为isa指针是一个受保护成员,你不能直接访问其他对象的isa指针。ReportFunction没有使用class方法是因为在一个类对象上调用这个方法是无法获得meta-class的,它只是返回这个类而已。(所以[NSString class]只是返回NSString类,而不是NSString的meta-class]

以下是程序的输出:

  1. This object is 0x10010c810.
  2. Class is RuntimeErrorSubclass, and super is NSError.
  3. Followingthe isa pointer 1times gives 0x10010c600
  4. Followingthe isa pointer 2times gives 0x10010c630
  5. Followingthe isa pointer 3times gives 0x7fff71038480
  6. Followingthe isa pointer 4times gives 0x7fff71038480
  7. NSObject's class is 0x7fff710384a8
  8. NSObject's meta class is 0x7fff71038480

观察通过isa获得的地址:

对象的地址是      0x10010c810.

类的地址是         0x10010c600.

类的meta-class地址是  0x10010c630.

类的meta-class的类地址是                 0x7fff71038480.(即NSOjbect的meta-class)

NSObject的meta-class的类地址是它自身。

这些地址的值并不重要,重要的是它们说明了文中讨论的从类到meta-class到NSObject的meta-class的整个流程。

结论:



meta-class是类对象的类,每个类都有自己单独的meta-class。所有的类对象并不会属于同一个meta-class。

meta-class要保证类对象具有继承体系中基类的所有实例和类方法,以及继承体系中的所有中间类方法。对于所有NSObject继承体系下的类,NSObject的实例方法和协议方法对他们和他们meta-class的对象都要有效。

所有的meta-class使用基类的meta-class作为自己的基类,对于顶层基类的meta-class也是一样,只是它指向自己而已。

详解Objective-C的meta-class 分类: ios相关 ios技术 2015-03-07 15:41 51人阅读 评论(0) 收藏的更多相关文章

  1. 周赛-Toy Cars 分类: 比赛 2015-08-08 15:41 5人阅读 评论(0) 收藏

    Toy Cars time limit per test 1 second memory limit per test 256 megabytes input standard input outpu ...

  2. SQL 分组 加列 加自编号 自编号限定 分类: SQL Server 2014-11-25 15:41 283人阅读 评论(0) 收藏

    说明: (1)日期以年月形式显示:convert(varchar(7),字段名,120) , (2)加一列 (3)自编号: row_number() over(order by 字段名 desc) a ...

  3. Winfrom传值 分类: C# 2015-07-22 15:41 1人阅读 评论(0) 收藏

    以前对WinForm窗体显示和窗体间传值了解不是很清楚  最近做了一些WinForm项目,把用到的相关知识整理如下  A.WinForm中窗体显示  显示窗体可以有以下2种方法:  Form.Show ...

  4. 相对路径 分类: C# 2015-06-11 15:41 8人阅读 评论(0) 收藏

    .绝对路径     绝对路径是指文件在硬盘上真正存在的路径.例如"bg.jpg"这个图片是存放在硬盘的"E:\book\网页布局代码\第2章"目录下,那么 &q ...

  5. ubuntu中安装eclipse 分类: android ubuntu linux 学习笔记 2015-07-07 10:19 75人阅读 评论(0) 收藏

    上一篇说了安装jdk的事,于是趁热打铁,决定把eclipse也安装了. 下载这一系列就不用说了. 下载完成之后: 然后解压,解压之后文件剪切到/usr/software文件夹中,同时重命名为eclip ...

  6. ubuntu中安装jdk 分类: java 学习笔记 linux ubuntu 2015-07-06 17:49 74人阅读 评论(0) 收藏

    参考文献:ubuntu 13.04 安装 JDK 先去oracle官网下载jdk,这一段我就不赘述了. 下载好之后先解压,解压方式参见 linux常用的压缩与解压缩命令 ,解压之后,将文件剪贴至/us ...

  7. PC机安装Qt以及QT交叉编译环境 分类: OpenCV ZedBoard shell ubuntu Eye_Detection 2014-11-08 18:57 246人阅读 评论(0) 收藏

    PC: apt-get install qtcreator Qt Embedded for ZedBoard: 下载qt-everywhere-opensource-src-4.7.3.tar.gz, ...

  8. javascript闭包获取table中tr的索引 分类: JavaScript 2015-05-04 15:10 793人阅读 评论(0) 收藏

    使用javascript闭包获取table标签中tr的索引 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...

  9. 安装hadoop1.2.1集群环境 分类: A1_HADOOP 2014-08-29 15:49 1444人阅读 评论(0) 收藏

    一.规划 (一)硬件资源 10.171.29.191 master 10.173.54.84  slave1 10.171.114.223 slave2 (二)基本资料 用户:  jediael 目录 ...

随机推荐

  1. list遍历

     一.对List的遍历有三种方式            List<String>    list    =    new    ArrayList<String>();    ...

  2. POJ 1365 Prime Land(整数拆分)

    题意:感觉题意不太好懂,题目并不难,就是给一些p和e,p是素数,e是指数,然后把这个数求出来,设为x,然后让我们逆过程输出x-1的素数拆分形式,形式与输入保持一致. 思路:素数打表以后正常拆分即可. ...

  3. Problem H: 小火山的围棋梦想 多校训练2(小火山专场)

    题目链接:http://acm.zzuli.edu.cn/zzuliacm/problem.php?id=1908 题意:如果'.'被'*'围起来,就把'.'变为'*'. 分析:如果是'*'直接输出, ...

  4. 转:selenium webdriver 执行javascript代码

    在用selenium webdriver 编写web页面的自动化测试代码时,可能需要执行一些javascript代码,selenium本身就支持执行js,我们在代码中import org.openqa ...

  5. 编程实现prim算法和Dijkstra算法。

    网址链接:http://blog.csdn.net/anialy/article/details/7603170

  6. mongodb综述

    摘要:mongodb相对于mysql的优势在于性能和分布式扩展方面

  7. cookies和session的优缺点

    具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案. Cookie的优缺点: 优点:极高的扩展性和可用性通过良好的编程,控制保存在cookie ...

  8. Mysql数据备份与恢复命令

    转载:原文地址 一.备份常用操作基本命令 1.备份命令mysqldump格式 格式:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 > 文件名.sq ...

  9. Problem - D - Codeforces Fix a Tree

    Problem - D - Codeforces  Fix a Tree 看完第一名的代码,顿然醒悟... 我可以把所有单独的点全部当成线,那么只有线和环. 如果全是线的话,直接线的条数-1,便是操作 ...

  10. 「CSS3 」3D效果 & 透视

    CSS3d透视 perspective属性 原文链接:https://segmentfault.com/a/1190000003843764