首页 > 其他分享 >iOS - Runtime-isa详解(位域、union(共用体)、位运算)

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

时间:2024-03-31 09:33:38浏览次数:19  
标签:person union iOS ZSXPerson BOOL 位域 isa Class

文章目录

iOS - Runtime-isa详解(位域、union(共用体)、位运算)

前言

本章主要了解Runtime相关内容,苹果对isa做了哪些优化,位域、union(共用体)又是如何运用的

  • 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
  • 在arm64架构之前,isa就是一个普通的指针,存储着ClassMeta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息

1. 位域介绍

利用好位域的话,可以使程序运行高效节省内存,操作系统级别的东西很多都会使用

在IM系统的开发中,就使用了位域来对数据进行优化,后续有时间再抽出案例聊聊。

欢迎点赞,收藏,加关注!,谢谢你!!

1.1 思路

假如ZSXPerson类需要3个BOOL类型的属性,这时候我们通常会直接使用@property声明3个属性,这时候系统会给我们生成 3个_开头的成员变量,3对getset方法,所占据的内存也比较多

思路:BOOL 类型属性值要么 YES 要么 NO,使用字节中的一个(0或者1)其实就能表示一个 BOOL 类型的属性值,一个字节就可以表示8个 BOOL 值

1.2 示例 - 结构体

ZSXPerson.h

@interface ZSXPerson : NSObject

- (void)setTall:(BOOL)tall;

- (BOOL)isTall;

- (void)setRich:(BOOL)rich;

- (BOOL)isRich;

- (void)setHandsome:(BOOL)handsome;

- (BOOL)isHandsome;

@end

ZSXPerson.m

@interface ZSXPerson() {
    struct {
        char tall: 1;
        char rich: 1;
        char handsome: 1;
    } _tallRichHandsome;
}

@end

@implementation ZSXPerson

- (void)setTall:(BOOL)tall {
    _tallRichHandsome.tall = tall;
}

- (BOOL)isTall {
    return !!_tallRichHandsome.tall;
}

- (void)setRich:(BOOL)rich {
    _tallRichHandsome.rich = rich;
}

- (BOOL)isRich {
    return !!_tallRichHandsome.rich;
}

- (void)setHandsome:(BOOL)handsome {
    _tallRichHandsome.handsome = handsome;
}

- (BOOL)isHandsome {
    return !!_tallRichHandsome.handsome;
}

@end

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZSXPerson *person = [[ZSXPerson alloc] init];
        person.tall = NO;
        person.rich = YES;
        person.handsome = YES;
        
        NSLog(@"tall:%d  rich:%d  handsome:%d", person.isTall, person.isRich, person.isHandsome);
    }
    return 0;
}

运行结果:

1.3 示例 - union(共用体)

ZSXPerson.h

@interface ZSXPerson : NSObject

- (void)setTall:(BOOL)tall;

- (BOOL)isTall;

- (void)setRich:(BOOL)rich;

- (BOOL)isRich;

- (void)setHandsome:(BOOL)handsome;

- (BOOL)isHandsome;

@end

ZSXPerson.h.m

#import "ZSXPerson.h"

#define ZSXTallMask (1)
#define ZSXRichMask (1 << 1)
#define ZSXHandsomeMask (1 << 2)

@interface ZSXPerson() {
    union {
        char bits;
        struct {
            char tall: 1;
            char rich: 1;
            char handsome: 1;
        };
    }_tallRichHandsome;
}

@end

@implementation ZSXPerson

- (void)setTall:(BOOL)tall {
    if (tall) {
        _tallRichHandsome.bits |= ZSXTallMask;
    }
    else {
        _tallRichHandsome.bits &= ~ZSXTallMask;
    }
}

- (BOOL)isTall {
    return !!(_tallRichHandsome.bits & ZSXTallMask);
}

- (void)setRich:(BOOL)rich {
    if (rich) {
        _tallRichHandsome.bits |= ZSXRichMask;
    }
    else {
        _tallRichHandsome.bits &= ~ZSXRichMask;
    }
}

- (BOOL)isRich {
    return !!(_tallRichHandsome.bits & ZSXRichMask);
}

- (void)setHandsome:(BOOL)handsome {
    if (handsome) {
        _tallRichHandsome.bits |= ZSXHandsomeMask;
    }
    else {
        _tallRichHandsome.bits &= ~ZSXHandsomeMask;
    }
}

- (BOOL)isHandsome {
    return !!(_tallRichHandsome.bits & ZSXHandsomeMask);
}

@end

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZSXPerson *person = [[ZSXPerson alloc] init];
        person.tall = NO;
        person.rich = YES;
        person.handsome = YES;
        
        NSLog(@"tall:%d  rich:%d  handsome:%d", person.isTall, person.isRich, person.isHandsome);
    }
    return 0;
}

运行结果:

1.3.1 说明

1.4 结构体 对比 union(共用体)

  • 结构体的成员是各自占用各自所需大小
  • 共同体的内存大小取决于其中最大的成员的大小,所有成员共用这块内存

  • 使用共用体实际上还是通过位运算来控制每个属性所占位置
  • 其中的sturct目的是增加可读性,实际上不会影响属性所占位置

2. arm64架构对isa的优化

arm64架构对isa中,使用一个64位的共用体来存储更多的信息,通过位域的概念来表示各个存储的信息的存储位置。其中有33位拿来存储Class、Meta-Class地址值


2.1 位域内容

nonpointer
  • 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
  • 1,代表优化过,使用位域存储更多的信息
has_assoc
  • 是否有设置过关联对象,如果没有,释放时会更快
has_cxx_dtor
  • 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
shiftcls
  • 存储着Class、Meta-Class对象的内存地址信息
magic
  • 用于在调试时分辨对象是否未完成初始化
weakly_referenced
  • 是否有被弱引用指向过,如果没有,释放时会更快
deallocating
  • 对象是否正在释放
extra_rc
  • 里面存储的值是引用计数器减1
has_sidetable_rc
  • 引用计数器是否过大无法存储在isa中
  • 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中

2.2 Class、Meta-Class对象存储位置

Class、Meta-Class对象存储在shiftcls,从上图可知shiftcls是从第4位开始,连续33

isa的掩码:define ISA_MASK 0x0000000ffffffff8ULL

将掩码转为二进制查看

它表示的就是从第4位开始,连续33位,通过这个掩码,就可以将Class、Meta-Class地址值取出来

Class、Meta-Class地址值后三位永远是 0。因为他的掩码左右边 3位是 0,&运算后一定是 0

3. 拓展

3.1 枚举值设计

在iOS中,系统的一些API可以使用|传入多个枚举值,比如:

self.view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin;

原理就是使用位域设计枚举值,然后通过位运算来取值

3.1.1 案例

我们也自己来设计一个这样的枚举

定义枚举:

typedef NS_OPTIONS(NSUInteger, ZSXOptions) {
    ZSXOptions1                 = 1 << 0, // 0b00000001
    ZSXOptions2                 = 1 << 1, // 0b00000010
    ZSXOptions3                 = 1 << 2, // 0b00000100
    ZSXOptions4                 = 1 << 3, // 0b00001000
    ZSXOptions5                 = 1 << 4, // 0b00010000
};

设置值方法:

- (void)setOptions:(ZSXOptions)options {
    if (options & ZSXOptions1) {
        NSLog(@"包含了ZSXOptions1");
    }
    
    if (options & ZSXOptions2) {
        NSLog(@"包含了ZSXOptions2");
    }
    
    if (options & ZSXOptions3) {
        NSLog(@"包含了ZSXOptions3");
    }
    
    if (options & ZSXOptions4) {
        NSLog(@"包含了ZSXOptions4");
    }
    
    if (options & ZSXOptions5) {
        NSLog(@"包含了ZSXOptions5");
    }
}

使用:

[self setOptions:ZSXOptions1 | ZSXOptions3 | ZSXOptions5];

打印如下:

此时我们已经实现了一个可以传入多个枚举值的接口

3.1.2 原理分析

/**
 0b00000001
 0b00000100
 0b00010000
 ----------------- | 运算(设置值)
 0b00010101
 
 0b00000001
 ----------------- & 运算(取值)
 0b00000001    为 true
 */

@oubijiexi

标签:person,union,iOS,ZSXPerson,BOOL,位域,isa,Class
From: https://blog.csdn.net/sharp521/article/details/137010961

相关文章

  • iOS - Runloop的运行逻辑
    文章目录iOS-Runloop的运行逻辑1.苹果官方的Runloop执行图2.Mode里面的东西2.1Source02.2Source12.3Timers2.4Observers3.执行流程3.1注意点4.Runloop休眠iOS-Runloop的运行逻辑1.苹果官方的Runloop执行图2.Mode里面的东西2.1Source0触摸事件......
  • Vue3+Vite+Axios Request 请求封装(TS版本)最新
    Vue3+Vite+AxiosRequest请求封装(TS版本)http>index.ts请求封装/**@Date:2024-03-3012:37:05*@LastEditors:zhong*@LastEditTime:2024-03-3014:12:52*@FilePath:\app-admin\src\http\index.ts*/importaxios,{AxiosInstance,AxiosRequestCon......
  • Ansible获取Cisco IOS版本导出至CSV
    1.inventory和密码加密参考AnsiblePlaybook备份Juniper及Cisco设备2.创建playbook-name:getciscoswitchesversionhosts:cisco_switchesgather_facts:falsevars_files:-pass.ymlvars:ansible_password:'{{cisco_pass}}'output_path:&qu......
  • nbtstat /?命令参数 显示使用NBT(NetBIOS over TCP/IP)的协议统计信息和当前TCP/IP连接
    NBTSTAT(NetBIOSoverTCP/IPStatistics)是一个用于显示和更新NetBIOS名称缓存、NetBIOS名称表和NetBIOS会话表的Windows命令行实用程序。它允许用户查看当前网络上的NetBIOS名称信息,以便诊断网络连接问题和执行基本的网络故障排除。NetBIOS是一种用于在局域网中进行通信的协议,它......
  • ios使用openlayer地图缩放时卡顿
    问题描述h5项目使用openlayer展示地图,并且使用VectorLayer铺点,安卓完全没问题,但是ios上缩放后会突然触发无法缩放并且无法点击拖动缓慢等问题。经排查,是VectorLayer的minZoommaxZoom导致,但不理解原因。问题代码如下importVectorLayerfrom'ol/layer/Vector'vectorLayer......
  • 【全开源】JAVA游戏陪玩系统源码陪练APP源码H5源码电竞系统源码支持Android+IOS+H5_博
    “游戏陪玩系统源码、陪练APP源码、H5源码及电竞系统源码”,这些创新技术的融合,为电竞爱好者与游戏玩家带来前所未有的互动体验。通过我们的源码,您可以轻松搭建起一个功能完备的游戏陪玩平台,无论是寻找技术高超的陪练,还是与志同道合的玩家组队竞技,都能在这个平台上得到满足。同......
  • 【全开源】JAVA多商户运营版商城系统源码地摊兄源码多商户源码社交电商源码支持Androi
    "多商户运营版商城系统源码——地摊兄源码多商户源码社交电商源码",是现今电商领域的一大突破。此源码设计独特,功能全面,既满足了多商户运营的需求,又融入了社交电商的理念。通过这套源码,商家可以轻松搭建起一个功能强大的电商平台,实现多商户的集中管理与运营,提升整体运营效率和用......
  • 【全开源】JAVA同城服务美容美发到店服务上门服务系统源码支持Android+IOS+H5_博纳软
    “同城服务美容美发到店服务上门服务系统”,这一创新服务模式正日益受到消费者的青睐。该系统以同城为范围,整合了美容美发行业的优质资源,为消费者提供了便捷、高效的到店与上门服务选择。无论是忙碌的上班族还是行动不便的老年人,只需轻轻一点,即可享受到专业的美容美发服务。同时......
  • 论文:Improving Entity Disambiguation by Reasoning over a Knowledge Base翻译笔记(通
    文章目录论文题目:通过在知识库中进行推理来改进实体消歧摘要1介绍2相关工作2.1带有知识库上下文的勃起功能障碍(ED)问题2.2基于知识图谱嵌入的ED2.3全局ED(实体解析)2.4多模块的实体识别3拟议的方法3.1任务表述3.2概述3.3提及表示3.4初始实体得分ψ~a~3.4.1实体......
  • Ajax 与 Axios 异步请求
    Ajax与Axios异步请求一、服务器对外提供了哪些资源1.网页中如何请求数据 数据,也是服务器对外提供的一种资源。只要是资源,必然要通过请求–处理–响应的方式进行获取。如果要在网页中请求服务器上的数据资源,则需要用到XMLHttpRequest对象。XMLHttpRequest(简称xhr)是......