Objective-C对象的分类

一、Objective-C对象的分类

Objective-C中的对象,简称OC对象,主要可以分为3种

  • instance对象(实例对象)
  • class对象(类对象)
  • meta-class对象(元类对象)

二、instance对象

1、instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象

// RevanPerson
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject {
    @public
    int _age;
}

@end

// ViewController
#import "ViewController.h"
#import "RevanPerson.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSObject *obj = [[NSObject alloc] init];
    RevanPerson *person = [[RevanPerson alloc] init];
    person->_age = 18;
    NSLog(@"%@\n%@", obj, person);
}

@end
  • obj是NSObject的instance对象,person是RevanPerson的instance对象;他们是不同的实例对象,分别占用两块不同内存

2、instance对象在内存中存储的信息包含

  • isa指针
  • 成员变量
    instance对象在内存职工存储信息.png

3、class对象(类对象)在内存中存储的信息

#import "ViewController.h"
#import "RevanPerson.h"

#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSObject *obj1 = [[NSObject alloc] init];
    NSObject *obj2 = [[NSObject alloc] init];
    
    Class obj_class1 = [obj1 class];
    Class obj_class2 = [obj2 class];
    Class obj_class3 = [NSObject class];
    
    //使用 runtime 获取[实例对象]的[类对象]
    Class obj_class4 = object_getClass(obj1);
    Class obj_class5 = object_getClass(obj2);
    
    NSLog(@"\n%p\n%p\n%p\n%p\n%p\n", obj_class1, obj_class2, obj_class3, obj_class4, obj_class5);
}

@end
打印输出:
0x106cdeea8
0x106cdeea8
0x106cdeea8
0x106cdeea8
0x106cdeea8
  • obj_class1、obj_class2、obj_class3、obj_class4、obj_class5都是NSObject的class对象(类对象),从打印输出可以他们都是相同的,
  • 每一个类在内存中有且只有一个class对象(类对象)
  • class对象在内存中存储的信息包括:
    • isa指针
    • superclass指针
    • 类的属性信息(@property)
    • 类的对象方法信息(instance method)
    • 类的协议信息(protocol)
    • 类的成员变量信息
      class对象内存存储信息.png

4、meta-class


#import "ViewController.h"
#import "RevanPerson.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSObject *revan_obj = [[NSObject alloc] init];
    RevanPerson *revan_person = [[RevanPerson alloc] init];
    
    Class NSObjectClass = [NSObject class];//类对象
    Class NSObjectClass1 = object_getClass(revan_obj);//类对象(runtime)
    Class NSObjectMetaClass = object_getClass(NSObjectClass);//元类对象(runtime)
    
    NSLog(@"\nNSObjectClass:%p\nNSObjectClass1:%p\nNSObjectMetaClass:%p\n", NSObjectClass, NSObjectClass1, NSObjectMetaClass);
    
    
    Class RevanPersonClass = [RevanPerson class];//类对象
    Class RevanPersonClass1 = object_getClass(revan_person);//类对象(runtime)
    Class RevanPersonMetaClass = object_getClass(RevanPersonClass);//元类对象(runtime)
    NSLog(@"\nRevanPersonClass:%p\nRevanPersonClass1:%p\nRevanPersonMetaClass:%p\n", RevanPersonClass, RevanPersonClass1, RevanPersonMetaClass);
}
@end

输出打印:
NSObjectClass:0x11099cea8
NSObjectClass1:0x11099cea8
NSObjectMetaClass:0x11099ce58

RevanPersonClass:0x10f9f2ee0
RevanPersonClass1:0x10f9f2ee0
RevanPersonMetaClass:0x10f9f2eb8

  • 使用runtime方法object_getClass(<#id _Nullable obj#>)来获取meta-class
    • 当obj传入的是instance对象时,获得的是class对象
    • 当obj传入的是class对象时,获得的是meta-class对象
  • 每个类在内存中有且只有一个meta-class对象
  • meta-class对象和class对象的内存结构是一样的(都是Class类型结构),但是用途不同
  • meta-class对象在内存中存储的主要信息包括
    • isa指针
    • superclass
    • 类方法信息
      meta-class内存存储信息.png

三、isa指针

在instance对象、class对象、meta-class对象的内存存储中都有一个isa指针,那么isa指针有什么用?

1、isa指针在instance对象中的作用
类的instance对象&class对象&meta-class对象.png

  • 情景:instance对象调用对象方法,这个过程是怎么实现的?
    • oc语言是一门动态语言也是一门消息语言,当一个instance对象调用一个方法的时候,底层的实现是给这个instance对象发送一个消息,从上图我们知道instance对象在内存中只存储了isa指针和成员变量,是没有存储方法的;对象方法信息是存储在class对象的内存中的,那么如何才能找到class对象?这时候就需要用到isa指针。系统会通过isa指针找到instance对象对应的class对象,从class对象的对象方法列表中查找是否有方法和刚才发出的方法匹配,如果匹配就执行方法,如果没有找到就会报错:unrecognized selector
  • instance的isa指向class
    • 当调用对象方法时,通过 instance对象的isa找到class对象,最后找到对象方法的实现进行调用
      instance对象中的isa.png
  • 测试源码
/***********  RevanPerson  *************/
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

- (void)instanceRevanPersonFun;
@end


#import "RevanPerson.h"

@implementation RevanPerson

@end

/***********  测试 instance对象调用对象方法  *************/
#import "ViewController.h"
#import "RevanPerson.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //instance对象
    RevanPerson *revan_p = [[RevanPerson alloc] init];
    //instance对象对用方法
    [revan_p instanceRevanPersonFun];
}
@end

2、isa指针在class对象中的作用

  • 情景:class对象调用类方法
    • 通过上面的图可以知道class对象的内存中并没有类方法的信息,类方法信息是存储在meta-class对象的内存中,这就需要使用class对象中的isa指针来寻找到meta-class对象,最后找到类方法的实现并进行调用,如果没有找到就会报错:unrecognized selector
  • class的isa指针指向meta-class
    • 当调用类方法,通过class的isa找到meta-class,最后找到类方法的实现并进行调用
      class对象中的isa.png
  • 测试源码
/************ RevanPerson *************/
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

/**
 对象方法
 */
- (void)instanceRevanPersonFun;
/**
 类方法
 */
+ (void)classRevanPersonFun;
@end

#import "RevanPerson.h"

@implementation RevanPerson

@end


/************ 测试代码 ************/
#import "ViewController.h"
#import "RevanPerson.h"
#import <objc/runtime.h>

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //调用类方法
    [RevanPerson classRevanPersonFun];
}
@end
  • 小结:isa指针在instance对象、class对象、meta-class对象中的作用:
    • instance对象的isa指向class对象
    • class对象的isa指向meta-class对象
      isa指针.png

四、superclass指针

通过创建RevanStudent类、RevanPerson类、NSObject类来分析superclass,并且RevanStudent类继承于RevanPerson类,RevanPerson类继承于NSObject
superclass.png

1、RevanStudent类的instance对象调用自己类中的对象方法在上面已经分析过了,由于RevanStudent类继承于RevanPerson类,那么RevanStudent类的instance对象可以调用RevanPerson类中的对象方法,同理也可以调用NSObject类中的对象方法,这个过程是如何完成的

  • 调用父类中的方法
/************ RevanPerson *************/
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

/**
 对象方法
 */
- (void)instance_RevanPersonFun;
@end

#import "RevanPerson.h"

@implementation RevanPerson
/**
 对象方法
 */
- (void)instance_RevanPersonFun {
    NSLog(@"instance_RevanPersonFun");
}
@end


/************ RevanPerson *************/
#import "RevanPerson.h"

@interface RevanStudent : RevanPerson

/**
 对象方法
 */
- (void)instance_RevanStudentFun;
@end

#import "RevanStudent.h"

@implementation RevanStudent

/**
 对象方法
 */
- (void)instance_RevanStudentFun {
    NSLog(@"instance_RevanStudentFun");
}

@end

/************ 测试代码 ************/

#import "ViewController.h"
#import "RevanPerson.h"
#import "RevanStudent.h"


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //创建revan_s
    RevanStudent *revan_s = [[RevanStudent alloc] init];
    //调用父类对象方法
    [revan_s instance_RevanPersonFun];
}
@end

输出打印:
instance_RevanPersonFun(RevanPerson类中的对象方法的输出)
  • 相对应instance对象(revan_s)来说,instance_RevanPersonFun对象方法是存储在RevanPerson类的class对象的内存中,而不是存储在RevanStudent类的class对象的内存中,所以肯定不是通过isa指针找到的instance_RevanPersonFun对象方法。superclass是用来指向父类。那么这个调用过程如下
    • revan_s实例对象通过isa指针找到class对象,并且查找对象方法信息,发现并没有instance_RevanPersonFun这个对象方法。再通过class对象中的superclass指针找到父类的class对象并从对象方法中查找,终于找到了instance_RevanPersonFun对象方法。如果还是没有找到就继续在父类的父类中寻找
      superclass用来指向父类.png

2、meta-class对象中的superclass

  • 情景:RevanStudent调用RevanPerson的类方法

/************ RevanStudent *************/
#import "RevanPerson.h"

@interface RevanStudent : RevanPerson

/**
 对象方法
 */
- (void)instance_RevanStudentFun;

/**
 类方法
 */
+ (void)class_RevanStudentFun;
@end
#import "RevanStudent.h"

@implementation RevanStudent

/**
 对象方法
 */
- (void)instance_RevanStudentFun {
    NSLog(@"instance_RevanStudentFun");
}

/**
 类方法
 */
+ (void)class_RevanStudentFun {
    NSLog(@"class_RevanStudentFun");
}
@end


/************ RevanPerson *************/
#import <Foundation/Foundation.h>

@interface RevanPerson : NSObject

/**
 对象方法
 */
- (void)instance_RevanPersonFun;
/**
 类方法
 */
+ (void)class_RevanPersonFun;
@end

#import "RevanPerson.h"

@implementation RevanPerson
/**
 对象方法
 */
- (void)instance_RevanPersonFun {
    NSLog(@"instance_RevanPersonFun");
}

/**
 类方法
 */
+ (void)class_RevanPersonFun {
    NSLog(@"class_RevanPersonFun");
}

@end


/************ 测试代码 ************/

#import "ViewController.h"
#import "RevanPerson.h"
#import "RevanStudent.h"


@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //调用父类的类方法
    [RevanStudent class_RevanPersonFun];
}
@end

打印输出:
class_RevanPersonFun(RevanPerson的类方法)
  • RevanStudent在调用类方法时,首先会通过RevanStudent类的class对象中的isa指针到RevanStudent类的meta-class对象的类方法中查找是否有这个被调用的类方法,如果没有就会通过RevanStudent类的meta-class对象中的superclass指针指向父类的meta-class对象在类方法中继续查找,如果找到就调用执行。如果没有找到就继续想父类的meta-class对象中的类方法中寻找
    meta-class对象中的superclass.png

五、总结
isa&&superclass.png

isa&&superclass.png
  • isa指针总结
    • instance对象的isa指针指向class对象
    • class对象的isa指针指向meta-class对象
    • meta-class的isa指针指向基类的meta-class对象
  • superclass指针总结
    • class对象的superclass指针指向父类的class对象,如果没有父类,superclass指针为nil
    • meta-class的superclass指针指向父类的meta-class对象
    • 基类的meta-class的superclass指针指向基类的class对象
  • instance调用对象方法
    • isa找到class对象,方法不存在,就通过superclass找到父类的class对象
  • class对象调用类方法
    • isa找到meta-class对象,方法不存在,就通过superclass找到父类的meta-class对象
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343