比较简单的一篇英文,重点是讲解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. 解决 UNMOUNTABLE_BOOT_VOLUME 蓝屏【转载】

    现象:一台XP系统的机器,开机在滚动条阶段蓝屏,蓝屏代码大概是“UNMOUNTABLE_BOOT_VOLUME”. 用PE进系统后发现C盘的文件格式变为RAW,总容量等变为0 解决方法一:将故障机的硬 ...

  2. IOS小工具以及精彩的博客

    IOS小工具以及精彩的博客 工具 Log Guru是一个收集Log的小工具, 可以在 Mac 上查看 iOS 设备的实时系统日志. 现在可以直接高亮显示在 FIR.im 上安装 app 失败的原因.后 ...

  3. HDU 3966 Aragorn's Story 动态树 树链剖分

    Aragorn's Story Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) ...

  4. hibernate事务并发问题(脏读,不可重复读,幻读)

    脏读  dirty read:  读了别的事务没有提交的事务, 可能回滚, 数据可能不对. 不可重复读 non repeatable read: 同一个事务里前后读出来的数据不一样, 被另一个事务影响 ...

  5. 关于oracle数据库的监听器配置问题

    一般我都会把ORACLE安装到E盘,监听器会自动安装到D盘,文件名叫 instantclient_12_1 监听器文件listener.ora的设置和tnsnames.ora设置 :http://bl ...

  6. Andorid APK反逆向解决方案---梆梆加固原理探寻

    本文章由Jack_Jia编写,转载请注明出处.   文章链接:http://blog.csdn.net/jiazhijun/article/details/8892635 作者:Jack_Jia    ...

  7. .htaccess 保护文件夹

    想要保护admin文件夹,经过以下两个步骤: 步骤一.可以用记事本新建文件.htaccess,输入以下内容: AuthType BasicAuth UserFile D:/AppServ/www/Hi ...

  8. thin-provisioning-tools

    公司我还用着squeeze,没这个包,下载编译:https://github.com/jthornber/thin-provisioning-tools.git

  9. 读 《我为什么放弃Go语言》 有感

    最近又熟悉了下go语言,发现go语言还有许多设计不好的地方,然后又读到了<我为什么放弃Go语言>这篇文章, 对于某些方面,我还是比较认同的. 这篇文章总结了十六点,如下: 1.1 不允许左 ...

  10. Python+Selenium使用Page Object实现页面自动化测试

    Page Object模式是Selenium中的一种测试设计模式,主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题 等),这样在Selenium测试页面中可以通 ...