详解Objective-C的meta-class 分类: ios相关 ios技术 2015-03-07 15:41 51人阅读 评论(0) 收藏
比较简单的一篇英文,重点是讲解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的子类,同时添加一个实例方法给它:
- Class newClass =
- objc_allocateClassPair([NSError class], "RuntimeErrorSubclass", 0);
- class_addMethod(newClass, @selector(report), (IMP)ReportFunction, "v@:");
- objc_registerClassPair(newClass);
函数ReportFunction就是添加的实例方法的具体实现,如下:
- void ReportFunction(id self, SEL _cmd)
- {
- NSLog(@"This object is %p.",self);
- NSLog(@"Class is %@, and super is %@.",[self class],[self superclass]);
- Class currentClass = [self class];
- for( int i = 1; i < 5; ++i )
- {
- NSLog(@"Following the isa pointer %d times gives %p",i,currentClass);
- currentClass = object_getClass(currentClass);
- }
- NSLog(@"NSObject's class is %p", [NSObject class]);
- NSLog(@"NSObject's meta class is %p",object_getClass([NSObject class]));
- }
看起来一切都很简单,运行时创建类只需要三步:
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中对象的定义是如下的样子:
- typedef struct objc_object {
- Class isa;
- }*id;
这个定义表明:任何以一个指向Class的指针作为首个成员的数据结构都可以被认为是一个objc_object.
最重要的特性就是,你可以向OC中的任何对象发送消息,如下这样:
- 【@”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字段。
- typedef struct objc_class *Class;
- struct objc_class{
- Class isa;
- Class super_class;
- /*followed by runtime specific details...*/
- };
为了可以调用类方法,这个类的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方法。
- id instanceOfNewClass = [[newClass alloc]initWithDomain:@"some Domain" code:0 userInfo:nil];
- [instanceOfNewClass performSelector:@"report)];
- [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]
以下是程序的输出:
- This object is 0x10010c810.
- Class is RuntimeErrorSubclass, and super is NSError.
- Followingthe isa pointer 1times gives 0x10010c600
- Followingthe isa pointer 2times gives 0x10010c630
- Followingthe isa pointer 3times gives 0x7fff71038480
- Followingthe isa pointer 4times gives 0x7fff71038480
- NSObject's class is 0x7fff710384a8
- 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) 收藏的更多相关文章
- 周赛-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 ...
- SQL 分组 加列 加自编号 自编号限定 分类: SQL Server 2014-11-25 15:41 283人阅读 评论(0) 收藏
说明: (1)日期以年月形式显示:convert(varchar(7),字段名,120) , (2)加一列 (3)自编号: row_number() over(order by 字段名 desc) a ...
- Winfrom传值 分类: C# 2015-07-22 15:41 1人阅读 评论(0) 收藏
以前对WinForm窗体显示和窗体间传值了解不是很清楚 最近做了一些WinForm项目,把用到的相关知识整理如下 A.WinForm中窗体显示 显示窗体可以有以下2种方法: Form.Show ...
- 相对路径 分类: C# 2015-06-11 15:41 8人阅读 评论(0) 收藏
.绝对路径 绝对路径是指文件在硬盘上真正存在的路径.例如"bg.jpg"这个图片是存放在硬盘的"E:\book\网页布局代码\第2章"目录下,那么 &q ...
- ubuntu中安装eclipse 分类: android ubuntu linux 学习笔记 2015-07-07 10:19 75人阅读 评论(0) 收藏
上一篇说了安装jdk的事,于是趁热打铁,决定把eclipse也安装了. 下载这一系列就不用说了. 下载完成之后: 然后解压,解压之后文件剪切到/usr/software文件夹中,同时重命名为eclip ...
- ubuntu中安装jdk 分类: java 学习笔记 linux ubuntu 2015-07-06 17:49 74人阅读 评论(0) 收藏
参考文献:ubuntu 13.04 安装 JDK 先去oracle官网下载jdk,这一段我就不赘述了. 下载好之后先解压,解压方式参见 linux常用的压缩与解压缩命令 ,解压之后,将文件剪贴至/us ...
- 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, ...
- javascript闭包获取table中tr的索引 分类: JavaScript 2015-05-04 15:10 793人阅读 评论(0) 收藏
使用javascript闭包获取table标签中tr的索引 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...
- 安装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 目录 ...
随机推荐
- UVALive 2323 Modular Multiplication of Polynomials(模拟)
这是一个相对简单的模拟,因为运算规则已经告诉了我们,并且比较简单,不要被吓到…… 思路:多项式除以另外一个多项式,如果能除,那么他的最高次一定被降低了,如果最高次不能被降低,那说明已经无法被除,就是题 ...
- $.ajax和$.post的区别(前者根据key-value/后者根据形参)
post不需要给key-value形式: $("#btn").click(function(){ var url=basePath+"/emp/login"; ...
- MVC3 Razor 根据不同页面使用不同Layout
_ViewStart.cshtml运行于每一Page前, 所以通常在这里先设置Layout 下面代码为特定的controller指定Index,Edit,Create的模板 即Index对应 _ ...
- JSP标准标签库(JSTL)--核心标签库 c
核心标签库是JSTL中最重要的部分,可以完成输出,判断,迭代等操作 功能分类: 1. 基本标签: <c:out>:输出属性内容 <c:set>:设置属性内容 <c:rem ...
- 解读QML之四
解读QML之四 QML对象属性 每一个QML对象类型都定义了一系列属性.每创建一个该对象类型的实例,该实例的这些属性也自动被创建了.接下来我们讨论几种不同类型的属性. id属性 每一个QML对象类型都 ...
- 关于submit与document.form1.submit();这2个提交的区别
首先要知道 一个是按钮提交 一个是在js函数里写代码 document.form1.submit() 提交 区别如下: 从使的方式及效主要有二点区别吧.一.使用submit()提交时,表单中不能存在s ...
- 我也谈javascript闭包
1.什么是闭包呢?Whenever you see the function keyword within another function, the inner function has acces ...
- 用JavaScript 来将数字转换成字符。
背景: 一切嵌入式设备上面的信息,比如设备名称,设备时区是可以写入到设备上面的寄存器中的(一个寄存器两个字节,2*8 bit),比如 -1 ,写入到寄存器中为 2d31,然后可以通过一些进程将寄存器中 ...
- 我也谈javascript闭包的原理理解
参考原文:http://www.oschina.net/question/28_41112 前言:还是一篇入门文章.Javascript中有几个非常重要的语言特性——对象.原型继承.闭包.其中闭包 对 ...
- Cannot open the disk 'F:\centos64-final\CentOS 64-bit\CentOS 64-bit.vmdk' orone of the snapshot disk
删除虚拟机系统目录下的 后缀名为 .lck 的文件或文件夹 这个lck文件是虚拟机的磁盘锁文件,我们知道虚拟机的磁盘与主机的磁盘是共存的,只是由于采用特定的虚拟机制,使二者互不影响.在使用虚拟机时,v ...