OC底层学习-Runtime
1. 概述
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
2.Runtime之 isa指针
- 要想学习Runtime,首先需要了解底层的一些常用的数据结构,比如isa指针
- 在arm64架构之前,isa就是 一个普通的指针,储存着Class,Meta-Class对象的内存地址
- 从arm64架构开始,对isa指针进行了优化,变成了一个共用体(
union)结构,还使用位域来存储更多的信息
2.1 示例:使用位来储存值
@interface GYPerson : NSObject
//我们不能声明一个属性,如果声明属性,系统会默认生成set和get方法
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;
@end
//
// GYPerson.m
// Runtime-isa
//
// Created by admin on 2020/9/18.
// Copyright © 2020 admin. All rights reserved.
//
#import "GYPerson.h"
#define GYTallMask (1<<0)
#define GYRichMask (1<<1)
#define GYHansomeMask (1<<2)
@implementation GYPerson
{
//声明一个char类型的一个字节来储存 3个Bool类型的变量值
char _tallRichHansome;
}
- (instancetype)init
{
self = [super init];
if (self) {
//初始化赋值 , 假设tall、rich、handsome 在_tallRichHansome变量中的储存位置是从右往左开始储存
_tallRichHansome = 0b00000100;
}
return self;
}
/**
位运算:
& : 与运算 在二进制下 都为1 得出的值是1, 如果有一个是0 那么值为0 (可以去除特定的位)
// 0000 0111
//&0000 0100
//------
// 0000 0100
*/
- (void)setRich:(BOOL)rich {
//1.rich是储存从右往左数第一位,那么我们如果得到右数第一位的值,这里我们需要用位运算
if (rich) {
//如果是YES 改变我们需要使用|运算 使用|运算不改边原来的值
_tallRichHansome |= GYRichMask;
} else {
_tallRichHansome &= ~GYRichMask;
}
}
- (void)setTall:(BOOL)tall {
if (tall) {
//如果是YES 改变我们需要使用|运算 使用|运算不改边原来的值
_tallRichHansome |= GYTallMask;
} else {
//如果原本是0 那我们需要去掩码的反码 在和原来的进行 &运算
/**
0000 0001 储存的值
0000 0001 掩码
1111 1110 掩码的反码
*/
_tallRichHansome &= ~GYTallMask;
}
}
- (void)setHandsome:(BOOL)handsome {
if (handsome) {
//如果是YES 改变我们需要使用|运算 使用|运算不改边原来的值
_tallRichHansome |= GYHansomeMask;
} else {
//如果原本是0 那我们需要去掩码的反码 在和原来的进行 &运算
/**
0000 0001 储存的值
0000 0001 掩码
1111 1110 掩码的反码
*/
_tallRichHansome &= ~GYHansomeMask;
}
}
- (BOOL)isRich {
//取值 使用& 运算 不管你&运算出来是多少,首先使用!把值变成0 在使用!变成1 Bool值 0和1
return !!(_tallRichHansome & GYRichMask);
}
- (BOOL)isTall {
return !!(_tallRichHansome & GYTallMask);
}
- (BOOL)isHandsome {
return !!(_tallRichHansome & GYHansomeMask);
}
@end
//测试代码
int main(int argc, const char * argv[]) {
@autoreleasepool {
GYPerson *person = [[GYPerson alloc] init];
person.tall = NO;
person.rich = YES;
person.handsome = NO;
NSLog(@"tall:%d rich:%d hansome:%d", person.isTall, person.isRich, person.isHandsome);
}
return 0;
}
- 位运算:
- &: 与
- |: 或
- !: 非
- ~: 取反
- ^: 异或
上述实现过程我们还可以实现,但是管理起来比较麻烦,我们还可以使用结构体和位域来实现上述功能:
/**
使用位域的技术来存储值
*/
@implementation GYPerson {
// 定义一个结构体类型的数据来储存数据
struct {
//这样表示该字段占据1位 , 储存顺序是从右往左开始 储存
char tall : 1;
char rich : 1;
char handsome : 1;
} _TallRichHandsome;
}
- (void)setTall:(BOOL)tall {
_TallRichHandsome.tall = tall;
}
- (void)setRich:(BOOL)rich {
_TallRichHandsome.rich = rich;
}
- (void)setHandsonme:(BOOL)handsome {
_TallRichHandsome.handsome = handsome;
}
- (BOOL)isTall {
//如果不加!! ,且值为1的话,那么直接取值为-1 ,这是因为
//因为只占一位 而返回的BOOL类型的值是一个字节 占8位, 那么系统会自动以这个一位的值来填充其他位(如果该位是1,就是拿1来填充其他位->0b1111 1111 -> -1,(255和-1 储存都是0xff,如果是无符号就是255,有符号就是-1)), 这里有两种解决方法,一种是加上!! ,第二种是用两位来存储,这样有一位是0,那么久会以0来填充其他位
return !!_TallRichHandsome.tall;
}
- (BOOL)isRich {
return !!_TallRichHandsome.rich;
}
- (BOOL)isHandsome {
return !!_TallRichHandsome.handsome;
}
@end
union共用体: 表示大家共用一块内存,接下来我们可以使用union来是实现上述存储功能, 使用结构体来表示说明
#define GYTallMask (1<<0)
#define GYRichMask (1<<1)
#define GYHandsomeMask (1<<2)
@implementation GYPerson {
//使用共用体来声明, 实际上和上面 声明 char _TallRichHandsome,的作用是一样的,都是表示一个字节
union {
char bits;
//该结构体仅表示代码说明的作用(增加代码的可读性),实际上不起任何作用的, 可以删除
//加上和这个结构体,是不影响以前的存储的,因为这个结构只占一个字节,union只占一个字节,所以这个结构体是没有影响union的内存的, 而且由始至终我们一直是在访问bits,没有去访问过结构体
// 如果结构体的内存超过一个字节, 那么bits的内存也需要相应的放生变化
struct {
char tall : 1;
char rich : 1;
char handsome : 1;
};
} _TallRichHandsome;
}
- (void)setTall:(BOOL)tall
{
if (tall) {
_TallRichHandsome.bits |= GYTallMask;
} else {
_TallRichHandsome.bits &= ~GYTallMask;
}
}
- (BOOL)isTall
{
return !!(_TallRichHandsome.bits & GYTallMask);
}
- (void)setRich:(BOOL)rich
{
if (rich) {
_TallRichHandsome.bits |= GYRichMask;
} else {
_TallRichHandsome.bits &= ~GYRichMask;
}
}
- (BOOL)isRich
{
return !!(_TallRichHandsome.bits & GYRichMask);
}
- (void)setHandsonme:(BOOL)handsonme
{
if (handsonme) {
_TallRichHandsome.bits |= GYHandsomeMask;
} else {
_TallRichHandsome.bits &= ~GYHandsomeMask;
}
}
- (BOOL)isHandsome
{
return !!(_TallRichHandsome.bits & GYHandsomeMask);
}
@end
-
存储值的位置是有xxxMask,也就是掩码来决定的
-
isa 的部分源码:
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
struct objc_object {
private:
isa_t isa;
}
union isa_t
{
isa_t() {
}
isa_t(uintptr_t value) : bits(value) {
}
Class cls;
uintptr_t bits;
# if __arm64__

本文详细介绍了Objective-C的Runtime机制,包括isa指针优化、消息发送流程、动态方法解析及消息转发机制等内容,并探讨了Runtime API的应用场景。
最低0.47元/天 解锁文章
3629





