文档章节

类的本质-类对象

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:17
字数 2992
阅读 1
收藏 0
点赞 0
评论 0

前言

今天整理了下自己电脑里的一些碎片笔记,时间有限只整理了这篇文章——类的本质,大家可以进行参考。

1.本质

  • 类的本质其实也是一个对象(类对象)
  • 程序中第一次使用该类的时候被创建,在整个程序中只有一份。
  • 此后每次使用都是这个类对象,它在程序运行时一直存在。
  • 类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本,继承层次,以及消息与函数的映射表等
  • 类对象代表类,Class类型,对象方法属于类对象
  • 如果消息的接收者是类名,则类名代表类对象
  • 所有类的实例都由类对象生成,类对象会把实例的isa的值修改成自己的地址,每个实例的isa都指向该实例的类对象

2.如何获取类对象

  • 通过实例对象

    格式:[实例对象 class];
    如:   [dog class];
  • 通过类名获取(类名其实就是类对象)

    格式:[类名 class];
    如:[Dog class]

3.类对象的用法

  • 用来调用类方法
[Dog test];

Class c = [Dog class];
[c test];
  • 用来创建实例对象
Dog *g = [Dog new];

Class c = [Dog class];
Dog *g1 = [c new];

4.类对象的存储


存储.png

5.OC实例对象、类对象、元数据、之间关系

  • Objective-C是一门面向对象的编程语言。

    • 每一个对象 都是一个类的实例。
    • 每一个对象 都有一个名为isa的指针,指向该对象的类。
    • 每一个类都描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。
    • 每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。
  • 在XCode中按Shift + Command + O打开文件搜索框,然后输入NSObject.h和objc.h,可以打开 NSObject的定义头文件,通过头文件我们可以看到,NSObject就是一个包含isa指针的结构体,如下图所示:
NSObject.h
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
objc.h
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};
  • 按照面向对象语言的设计原则,所有事物都应该是对象(严格来说 Objective-C并没有完全做到这一点,因为它有象int,double这样的简单 变量类型)
    • 在Objective-C语言中,每一个类实际上也是一个对象。每一个类也有一个名为isa的指针。每一个类都可以接受消息,例如[NSObject new],就是向NSObject这个类发送名为new的消息。
    • 在XCode中按Shift + Command + O,然后输入runtime.h,可以打开Class的定义头文件,通过头文件我们可以看到,Class也是一个包含isa指针的结构体,如下图所示。(图中除了isa外还有其它成员变量,但那是为了兼容非2.0版的Objective-C的遗留逻辑,大家可以忽略它。)
runtime.h
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
  • 因为类也是一个对象,那它也必须是另一个类的实例,这个类就是元类 (metaclass)。

    • 元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。
    • 元类(metaclass)也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,所有的元类的isa指针都会指向一个根元类(root metaclass)。
    • 根元类(root metaclass)本身的isa指针指向自己,这样就行成了一个闭环。上面说􏰀到,一个对象能够接收的消息列表是保存在它所对应的类中的。在实际编程中,我们几乎不会遇到向元类发消息的情况,那它的isa 指针在实际上很少用到。不过这么设计保证了面向对象的干净,即所有事物都是对象,都有isa指针。
    • 由于类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。
  • 下面这张图或许能够 让大家对isa和继承的关系清楚一些

其中:实线箭头代表类的继承关系,比如EOCStudent继承自EOCPerson,也就是说,EOCStudent是EOCPerson的子类。就可以用实线表示这种继承关系:EOCStudent —>EOCPerson。

虚线箭头代表对象和类的从属关系,比如一个对象student属于EOCStudent类,也就是说,student是EOCStudent的实例。就可以用虚线表示这种从属关系:student—>EOCStudent。

引用《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中的一段话:superclass指针确定了继承关系,而isa指针描述了实例所属的类。通过这张布局关系图即可进行“类型信息查询”。我们能查出对象是否能够响应某个选择子(selector),是否遵从某项协议,并且能够看出某对象位于集成体系的哪一部分


继承/从属关系图
  • 上图中,最让人困惑的莫过于Root Class了。在实现中,Root Class是指
  • NSObject,我们可以从图中看出:
  • NSObject类对象包括它的对象实例方法。
  • NSObject的元对象包括它的类方法,例如new方法。
  • NSObject的元对象继承自NSObject类。
  • 一个NSObject的类中的方法同时也会被NSObject的子类在查找方法时找到。

当对象收到消息时,消息函数首先根据该对象的isa
指针找到该对象所对应的类的方法表,并从表中寻找该消息对应的方法selector。如果找不到,objc_msgSend
将继续从父类中寻找,直到NSObject
类。一旦找到了方法选标, objc_msgSend
则以消息接收者对象为参数调用,调用该选标对应的方法实现。调用方式:objc_msgSend(receiver, selector, arg1, arg2, ...)
这就是在运行时系统中选择方法实现的方式。在面向对象编程中,一般称作方法和消息动态绑定的过程。
为了加快消息的处理过程,运行时系统通常会将使用过的方法选标和方法实现的地址放入缓存中。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。消息函数会首先检查消息接收者对象对应的类的缓存(理论上,如果一个方法被使用过一次,那么它很可能被再次使用)。如果在缓存中已经有了需要的方法选标,则消息仅仅比函数调用慢一点点。如果程序运行了足够长的时间,几乎每个消息都能在缓存中找到方法实现。程序运行时,缓存也将随着新的消息的增加而增加。

6.如何查询类型信息

可以使用“类型信息查询方法”来查询类的继承体系。其中,“isMemberOfClass:”可以判断对象是否是特定类的实例。而”isKindOfClass:”可以判断对象是否是某个类或者其派生子类的实例。而本质上,这两个类型信息查询方法是使用对象的isa指针获取对象所属的类(因为类对象也是对象,所以也有isa指针,该指针指向元类,也就是类对象所属的类),然后通过类继承体系中的superclass指针在继承体系中游走。Objective-C与其他语言不同,Objective-C必须查询类型信息,才能完全了解对象的真实类型。

另外,需要注意的是,我们从集合对象(collection)中获取的对象,通常会用到这两个查询类型信息的方法。因为从集合对象中取出来的对象不是强类型的(strongly typed),其类型通常是id。回想一下,我们从一个数组中取出来的对象,其返回值是id类型的。这就是为什么我们可以在这个取出来的对象身上通过中括号”[ ]”的形式调用任何方法,却不能通过点语法来调用方法。不过,为了安全起见,如果涉及到对集合对象中的某个对象进行操作,我们还是需要做一下类型判断比较好。如下所示:

    for (id object in array) {
        if (object isKindOfClass:[NSString class]) {
            // object is an instance of NSString
        }
    }

当然,也可以用比较类对象是否等同的方法来判断对象是否属于某个类。若是如此,那就应该使用==操作符,而不要使用比较Objective-C对象使常用的“isEqual:”方法。因为==操作符比较的是指针是否相等,也就是比较内存地址是否相同。而"isEqual:"比较的是两个Objective-C对象的值是否相等。此处用==操作符,原因在于,类对象类对象是“单例”,在应用程序范围内,每个类的Class仅有一个实例,在整个内存中仅有一份(因为+(void)load方法和+ (void)initialize只被调用一次)。所以也可以用下面这种方进行比较:

if ([object class] == [EOCSomeClass class]) {
        // object is an instance of EOCSomeClass
}

虽然调用class方法和isKindOfClass:方法都可以查询一个对象的类型。但是还是建议使用后者。下面笔者引用《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中的一段话来进行解释:

虽然使用"class方法"也可以查询对象的类型信息。但是还是建议使用isKindOfClass:这样的类型信息查询方法。因为后者可以正确处理那些使用了消息传递机制对象。比方说某个对象可能会把其的所有选择子(selector)都转发给另一个对象(开启了消息转发功能)。这样的对象叫做”代理(proxy)“,此种对象所属的类均以NSProxy为根类(root class)。通常情况下,如果在此种代理对象上调用class方法,那么返回的是代理对象本身(NSProxy的子类),而非接受代理的对象所属的类。然而,若是改用“isKindOfClass:”这样的类型信息查询方法,那么代理对象就会把这条消息转给“接受代理的对象(proxy object)”。也就是说,这条消息(指isKindOfClass:)的返回值与直接接受代理的对象身上查询其类型信息所得的结果相同。因此,这样查出来的类对象与直接通过class方法所返回的那个类对象不同,class方法所返回类表示发起代理的对象,而非接受代理的对象

文/VV木公子(简书作者)
PS:如非特别说明,所有文章均为原创作品,著作权归作者所有,转载请联系作者获得授权,并注明出处,所有打赏均归本人所有!

如果您是iOS开发者,或者对本篇文章感兴趣,请关注本人,后续会更新更多相关文章!敬请期待!

本文转载自:http://www.jianshu.com/p/374b570e1920

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2634
码字总数 82983
作品 0
程序员
面向对象的基本特征

面向对象方法具有三个基本特征: 封装:封装是将对象的实现细节隐藏起来,然后通过一些公共的方法来暴露该对象的功能。 继承:继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类...

sunfish
2016/11/26
8
0
iOS底层原理总结 - 探寻Runtime本质(三)

方法调用的本质 本文我们探寻方法调用的本质,首先通过一段代码,将方法调用代码转为c++代码查看方法调用的本质是什么样的。 通过上述源码可以看出c++底层代码中方法调用其实都是转化为 函数...

xx_cc
07/02
0
0
面向对象语言-反射机制

Java和C#是目前面向对象语言的代表性语言,Java语言是面向语言是代表。 今天主要就这两个面向对象语言的反射机制讨论一番, 其实要想了解反射本质,必须要深入的了解Java虚拟机和C#的运行时机...

abaojin
2015/12/19
211
0
iOS底层原理总结 - 探寻Class的本质

iOS底层原理总结 - 探寻Class的本质 对小码哥底层班视频学习的总结与记录。面试题部分,通过对面试题的分析探索问题的本质内容。 上接 iOS底层原理总结 - 探寻OC对象的本质 Class的本质 我们...

xx_cc
04/14
0
0
Java--反射的逐步理解

层层引入反射的作用 一.类类型的概念:所有类都是对象,是Class类的实例对象,这个对象我们成为该类的类类型 1.下面是一个小的test,以产生3种方式的类类型: foo user = Class c1 = foo. C...

sshpp
2017/07/24
0
0
Javaweb开发总结

如何开发浏览器? B/S比C/S好 动态脚本语言: JSPASP PHP 客户端技术:HTML CSS(叠层样式表) flash 客户端标本语言:JavaScript(Ajax里的) vbscript 服务端技术:CGI(过时了) ASP(微软的,不...

菜牛修炼之道
2017/12/20
0
0
设计模式 ——— 适配器模式

ADAPTER(适配器) ———— 类对象结构型模式 意图 将一个类的接口,转换成客户期望的另一个接口。适配器让原来接口不兼容的类可以合作无间。 现在,我们知道,这个模式可以通过创建适配器进行...

tomas家的小拨浪鼓
2017/11/11
0
0
Java基础巩固笔记(7)-多线程之共享数据

Contents java基础巩固笔记(5)-多线程之共享数据 线程范围内共享数据 多线程访问共享数据 本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的...

卟想苌亣
2017/12/04
0
0
Android动画:这里有一份很详细的 属性动画 使用攻略

前言 动画的使用 是 开发中常用的知识 本文将详细介绍 动画中 属性动画的原理 & 使用 动画类型 目录 目录 1. 属性动画出现的原因 属性动画()是在 ()后才提供的一种全新动画模式 那么为什...

Carson_Ho
2017/09/05
0
0
Android开发之道(10)Handler本质简析与使用实例

Android 开发之道(10)Handler本质简析与使用实例 作者:柳大·Poechant 邮箱:zhongchao.ustc@gmail.com 博客:blog.csnd.net/poechang 日期:March 19th, 2012 1 Overview 先浅析本质太过...

晨曦之光
2012/04/24
67
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

rabbitmq学习记录(六)交换机Exchange-direct

实现功能:一条消息发送给多个消费者 交换机模式:direct 相比于之前的fanout模式,可以进一步的筛选获取消息的消费者。 fanout模式下,只要消费者监听的队列,已经与接收生产者消息的交换机...

人觉非常君
16分钟前
0
0
Java 之 枚举

Java 中声明的枚举类,均是 java.lang.Enum 类的子类,Enun 类中的常用方法有: name() 返回枚举对象名称 ordinal() 返回枚举对象下标 valueOf(Class enumType, String name) 转换枚举对象 ...

绝世武神
24分钟前
0
0
使用爬虫实现代理IP池之放弃篇

啥叫代理IP以及代理IP池 概念上的东西网上搜索一下就好了,这里简单科普一下(大部分会读这篇文章的人,基本是不需要我来科普的),白话说就是能联网并提供代理访问互联网的服务器,它提供的...

一别丶经年
40分钟前
0
0
sqoop导入数据到Base并同步hive与impala

使用Sqoop从MySQL导入数据到Hive和HBase 及近期感悟 基础环境 Sqool和Hive、HBase简介 Sqoop Hive HBase 测试Sqoop 使用Sqoop从MySQL导入数据到Hive 使用复杂SQL 调整Hive数据类型 不断更新 ...

hblt-j
今天
0
0
Dart 服务端开发 文件上传

clent端使用angular组件 upload_component.html form id="myForm" method="POST" enctype="multipart/form-data"> <input type="file" name="fileData"> <!-- file field --></form>......

scooplol
今天
0
0
apache和tomcat同时开启,乱码问题

tomcat和apache同时开启,会走apache的转发,执行的是AJP/1.3协议。所以在tomcat的配置文件server中, <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" useBodyEncodingForU......

Kefy
今天
0
0
使用ssh-keygen和ssh-copy-id三步实现SSH无密码登录 和ssh常用命令

ssh-keygen 产生公钥与私钥对. ssh-copy-id 将本机的公钥复制到远程机器的authorized_keys文件中,ssh-copy-id也能让你有到远程机器的home, ~./ssh , 和 ~/.ssh/authorized_keys的权利 第一步...

xtof
今天
0
0
orcale 查询表结构

SELECT t.table_name, t.colUMN_NAME, t.DATA_TYPE || '(' || t.DATA_LENGTH || ')', t1.COMMENTS FROM User_Tab_Cols t, User_Col_Comments t1WHERE t.table_name......

wertwang
今天
0
0
华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大

华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大!华为nova3超级慢动作酷玩抖音,没有办法我就是这么强大! 在华为最新发布的nova 3手机上,抖音通过华为himedia SDK集成了60fps、超级...

华为终端开放实验室
今天
0
0
多 SSH Key 实现同一台服务器部署多 Git 仓库

本文以以下需求为背景,介绍详细的做法: 需在同一台服务器同时部署两个不同的 Github 仓库(对 Bitbucket 等 git 服务同样适用) root 用户可在远程登录 SSH 后附上预期的 SSH Key 进行 gi...

yeahlife
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部