在这篇文章中,我关注的是 Objective-C 中的一个陌生的概念—— meta-class。在 Objective-C 中的每个类都有一个相关联的 meta-class,但是你很少会直接使用 meta-class,他们仍旧保持着神秘的面纱。我们

“”

转自6david9的博客
 
在这篇文章中,我关注的是 Objective-C 中的一个陌生的概念—— meta-class。在 Objective-C 中的每个类都有一个相关联的 meta-class,但是你很少会直接使用 meta-class,他们仍旧保持着神秘的面纱。我们从在运行时创建一个类开始。通过查看 “class pair”,我会解释 meta-class 是什么,同时也会谈谈在 Objective-C 中的对象或者类相关的一些一般主题。
 
在运行时创建一个类
 
下面的代码在运行时创建了一个 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、为这个类添加所需的 methods 和 ivars(我已经使用 class_addMethod 添加过一个方法了)。
3、注册这个类,然后就可以使用了(使用 objc_registerClassPair)。
 
然后,中级问题是:“class pair” 是什么?函数 objc_allocateClassPair 只返回了一个值:这个 class。这一对中的另一个在哪?(译注:pair 有 “一对,一双” 的意思)
 
我敢肯定你已经猜到了另一半就是 meta-class(就是这篇文章的标题),但是要解释那是什么和你为什么需要它,我需要介绍一些在 Objective-C 中的关于对象和类的背景知识。
 
把一个数据结构变为对象需要什么?
 
每个对象都有一个类。这是面相对象概念的基础知识,但在 Objective-C 中不是这样,它(译注:class)同样是这个数据的一部分。每个可以被当成对象的数据结构都在恰当的位置有一个指向一个类的指针。
 
在 Objective-C,一个对象的类由它的 isa 指针决定。isa 指针指向这个对象的 Class。
 
事实上,在 Objective-C 中的对象的定义看起来像这样:
  1. typedef struct objc_object {
  2. Class isa;
  3. } *id;
 
这就是说:任何结构体只要以一个指向 Class 结构的指针开始的就可以被当成是 objc_object。
 
在 Objective-C 中的对象的一个重要的特性是,你可以向它们发送消息:
  1. [@"stringValue"
  2. writeToFile:@"/file.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
 
你可以这么做是因为,当你向一个 Objective-C 的对象(像这里的 NSCFString)发送消息的时候,runtime 沿着对象的 isa 指针找到了这个对象的 Class(这里是 NSCFString 的类)结构体。 Class 结构体中包含了一个这个类的方法列表和一个指向父类的指针,用于查找继承的方法。
 
关键点是 Class 结构体中定义了你可以向一个对象发送的消息。
 
meta-class 是什么?
 
现在,你可能已经知道,在 Objective-C 中一个 Class 也是一个对象。这就意味着你也可以向一个 Class 发送消息。
  1. NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];
 
这里,向 NSString 类发送了 defaultStringEncoding。
 
可以这么做是因为在 Objective-C 中每个 Class 它自己同样也是个对象。也就是说 Class 结构体必须以 isa 指针开始,然后就可以在二进制兼容(binary compatible)我上面介绍的 objc_object 结构了,接着下一个字段必须是一个指向它的父类的指针(要是类就是基类就是 nil)。
 
我上周已经介绍过,定义一个类有好几种方法,主要依赖于你正在运行的 runtime 的版本。但,是的,都是由一个 isa 字段开始然后是 superclass 字段。
  1. typedef struct objc_class *Class;
  2. struct objc_class {
  3. Class isa;
  4. Class super_class;
  5. /* 以下依赖于 runtime 的具体实现 …… */
  6. };
 
然而,为了让我们在 Class 上调用一个方法,Class 的 isa 指针必须指向一个 Class 结构体,并且那个 Class 结构体必须包含我们可以在那个 Class 上调用的方法的列表。
 
这就引出了 meta-class 的定义:meta-class 是 Class 对象的类(the meta-class is the class for a Class object)。
 
简单来说:
 
1.当你向一个对象发送消息,就在那个对象的方法列表中查找那个消息。
2.当你想一个类发送消息,就再那个类的 meta-class 中查找那个消息。
 
meta-class 是必须的,因为它为一个 Class 存储类方法。每个类都必须有一个唯一的 meta-class,因为每个 Class 都有一个可能不一样的类方法。
 
meta-class 的类是什么?
 
meta-class,如之前的 Class,同样是个对象。这就意味着你也可以在它上面调用方法。自然的,这就意味着它也必须有一个类(译注:isa 指针)。
 
所有的 meta-class 使用它们基类的 meta-class (继承层次中最顶层的 Class 的 meta-class)作为它们自己的类。这就是说所有继承自 NSObject 的类(大部分的类),以 NSObject 的 meta-class 作为自己的 meta-class 的类。
 
遵循这个规则,所有的 meta-class 使用基类的 meta-class 作为他们的类,任何基类的 meta-class 将会是他们自己(它们的 isa 指向他们自己)。这就是说 NSObject 的 meta-class 的 isa 指针指向它们自己(是自己的一个实例)。
 
class 和 meta-class 的继承
 
和 Class 以 super_class 指针指向它的父类的方法一样,meta-class 以 super_class 指针指向 Class 的 super_class 的 meta-class。(译注:这句话有点绕,就是 super-class 一个指向 Class 的父类,一个指向 meta-class 的父类。Class 是一般对象的类型,meta-class 是 Class 的类型。)
 
进一步来讲,基类的 meta-class 设置 super_class 指针指向基类自己。
 
这个继承层次的结果就是,所有在这个继承层次中的的实例,类和 meta-class 都继承了基类的层次。
 
对于所有在 NSObject 层次中的实例,类和 meta-class,这就意味着所有 NSObject 的实例方法都是有效的。对于类和 meta-class,所有 NSObject 的类方法也同样是有效的。
 
所有这些在字面上相当让人困惑。Greg Parker 已经把实例,类,meta-class 还有他们的超类以非常棒的图解的方式聚合在一起,展示他们是如何在一起工作的。
 
用实验验证这点
 
为了验证这些,让我们看看在我文章开头提供的 ReportFunction 的输出。这个函数的目的是顺着 isa 指针打引出它找到的。
 
要运行 ReportFunction,我们需要为这个动态创建的类创建一个实例,然后在上面调用这个方法。
  1. id instanceOfNewClass =
  2. [[newClass alloc] initWithDomain:@"someDomain" code:0 userInfo:nil];
  3. [instanceOfNewClass performSelector:@selector(report)];
  4. [instanceOfNewClass release];
 
因为没有这个方法的声明,所以我使用 performSelector: 调用这个方法,这样编译器就不会输出警告了。
 
现在 ReportFunction 会沿着 isa 指针告诉我们这个对象使用了哪些类,meta-class 和 meta-class 的类。
 
获取一个对象的类:ReportFunction 使用 object_getClass 跟随 isa 指针,因为 isa 指针是一个类中一个受保护的成员变量(你不能直接访问其他对象的 isa 指针)。ReportFunction 没有以类方法的形式这样调用,因为在 Class 对象上调用类方法不会返回 meta-class,而是再次返回 Class 对象(所以 [NSString class] 会返回 NSString 的类而不是 NSString
的 meta-class)。
 
这个是程序运行后的结果(省去了 NSLog 的前缀)。
  1. This object is 0x10010c810.
  2. Class is RuntimeErrorSubclass, and super is NSError.
  3. Following the isa pointer 1 times gives 0x10010c600
  4. Following the isa pointer 2 times gives 0x10010c630
  5. Following the isa pointer 3 times gives 0x7fff71038480
  6. Following the isa pointer 4 times gives 0x7fff71038480
  7. NSObject's class is 0x7fff710384a8
  8. NSObject's meta class is 0x7fff71038480
 
看看通过递归的查看 isa 的地址:
 
the object is address 0x10010c810.
the class is address 0x10010c600.
the meta-class is address 0x10010c630.
the meta-class’s class (i.e. the NSObject meta-class) is address 0x7fff71038480.
the NSObject meta-class’ class is itself.
地址的值不是很重要,只是演示了上面讨论的从类到 meta-class 到 NSObject 的 meta-class 的过程。
 
结论
 
meta-class 是 Class 对象的类。每个 Class 都有个不同的自己的 meta-class(因此每个 Class 都可以有一个自己不同的方法列表)。也就是说每个类的 Class 不完全相同。
 
meta-class 总是会保证 Class 对象会有从基类继承的所有的的实例和类方法,加上之后继承的类方法。如从 NSObject 继承的类,就意味着在所有的 Class(和 meta-class)对象中定义了所有从 NSObject 继承的实例和协议方法。
 
所有的 meta-class 使用基类的 meta-class(NSObject 的 meta-class 用于继承自 NSObject 的类)作为他们自己的类,包括在运行时自己定义的基础的 meta-class。

Objective-C 中的 Meta-class 是什么?的更多相关文章

  1. HTML中的meta(转载)

    meta是用来在HTML文档中模拟HTTP协议的响应头报文.meta 标签用于网页的<head>与</head>中,meta 标签的用处很多.meta 的属性有两种:name和 ...

  2. 使用CSS中的meta实现web定时刷新或跳转的方法

    这篇文章主要介绍了使用CSS中的meta实现web定时刷新或跳转的方法,比使用JavaScript脚本实现起来更加简单一些,需要的朋友可以参考下 meta源信息功能之页面定时跳转与刷新 几乎所有的网页 ...

  3. 理解Objective C 中id

    什么是id,与void *的区别 id在Objective C中是一个类型,一个complier所认可的Objective C类型,跟void *是不一样的,比如一个 id userName, 和vo ...

  4. 浅谈Objective—C中的面向对象特性

    Objective-C世界中的面向对象程序设计 面向对象称程序设计可能是现在最常用的程序设计模式.如何开发实际的程序是存在两个派系的-- 面向对象语言--在过去的几十年中,很多的面向对象语言被发明出来 ...

  5. objective C中的字符串NSStirng常用操作

    objective C中的字符串操作 在OC中创建字符串时,一般不使用C的方法,因为C将字符串作为字符数组,所以在操作时会有很多不方便的地方,在Cocoa中NSString集成的一些方法,可以很方便的 ...

  6. HTML中的<meta>标签的使用

    HTML中<meta>标签的使用 在我们制作的网页中,要是想让它能够让更多的人去访问,最好的方法就是通过搜索引擎来找到你的网址,于是需要你的网页可以有关键词能够让搜索引擎来识别,于是HTM ...

  7. html中的meta详解

    1  name=viewport <meta name="viewport" content="width=device-width,initial-scale=1 ...

  8. Objective C中的ARC的修饰符的使用---- 学习笔记九

    #import <Foundation/Foundation.h> @interface Test : NSObject /** * 默认的就是__strong,这里只是做示范,实际使用时 ...

  9. 移动web app 中的meta 标签

    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scal ...

  10. 清除ASPX页面中的meta:resourceKey="[a-zA-Z0-9]+"

    在替换对话框中,选中“使用正则表达式”, 被替换内容,使用 meta:resourceKey="[a-zA-Z0-9]+" 然后替换整个文档就可以了.

随机推荐

  1. PL/SQL Developer 查看查询的执行计划

    https://zhuanlan.zhihu.com/p/65771352 通过 PL/SQL Developer 查看查询的执行计划 1 什么是执行计划 执行计划是一条查询语句在 Oracle 中的 ...

  2. 13. this关键字

    1.this的概述 this关键字代表是对象的引用.也就是this在指向一个对象,所指向的对象就是调用该函数的对象引用. 2.this实例,初始化成员变 class Employee { privat ...

  3. csp-s模拟测试95

    csp-s模拟测试95 去世场祭. $T1$:这不裸的除法分块吗. $T2$:这不裸的数据结构优化$Dp$吗. $T3$:这不裸的我什么都不会搜索骗$30$分吗. 几分钟后. 这除法分块太劲了..(你 ...

  4. windows中创建流量转发规则

    #创建流量转发,其中listenaddress为内网ip(也就是ipconfig中所显示的IP),listenport为监听的本机对外端口,后两个分别是需转发的目标主机IP和端口. netsh int ...

  5. C#-Api:身份证识别

    ylbtech-C#-Api:身份证识别 通过POST上传base64格式的图片内容,可识别二代身份证.驾照.行驶证.军官证.中华人民共和国往来港澳通行证.台湾居民往来大陆通行证.大陆居民往来台湾通行 ...

  6. jquery中typeof的用法

    typeof 可以用来检测给定变量的数据类型,可能的返回值: 'undefined' 'boolean' 'string' 'number' 'object' 'function' var hahah ...

  7. JS-copy到剪贴板

    因为 clipboard.js 兼容性受限

  8. axios全局拦截响应

    在系统开发过程中,若遇到长时间未操作,则需要将页面跳转到登录页面.因为现在都是前后端分离的开发模式,路由跳转都交给前端,而后端只返回一个报错信息,例如"errorMsg":&quo ...

  9. 1-MySQL高级-视图

    视图 1. 问题 对于复杂的查询,往往是有多个数据表进行关联查询而得到,如果数据库因为需求等原因发生了改变,为了保证查询出来的数据与之前相同,则需要在多个地方进行修改,维护起来非常麻烦 解决办法:定义 ...

  10. The Preliminary Contest for ICPC Asia Xuzhou 2019 G. Colorful String 回文树

    签到提: 题意:求出每一个回文串的贡献 (贡献的计算就是回文串不同字符的个数) 题解: 用回文树直接暴力即可 回文树开一个数组cost[ ][26] 和val[ ] 数组: val[i]表示回文树上节 ...