OC底层学习-Runtime

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

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__
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值