Runtime系列:位运算在OC中的取值和赋值【01】

简介: 本片文章介绍将数据(如:Bool类型的数据)存储到二进制位中,包括实现逻辑、代码示例以及各自的优缺点分析。

一、实现逻辑


我们可以用简单的Bool类型,来实现数据在二进制中的取值和赋值:


1、取值

掩码 + 位与(&)运算。


/** 
 *
 *   0000 0111
 * & 0000 0010
 *-------------
 *   0000 0010
 *  
 *  二进制转Bool类型需要两次取反(!!)
 */


2、赋值

2.1、当设置值为1时

掩码 + 位或(|)运算。


/**
 *   0000 0101
 * | 0000 0010
 *-------------
 *   0000 0111
 */


2.2、当设置值为0时

掩码取反(~) + 再进行位与(&)运算。


/**
 *   0000 0111
 * & 1111 1101
 *-------------
 *   0000 0101
 */


知道了位运算的基本规则,下面用几个例子,来了解一下位运算在OC中的应用:


二、位运算符赋值


这里我们就用高富帅(tall、rich、handsome)的取值和赋值方法,作为举例。


2.1、代码示例

Person.h


#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
// 高
- (BOOL)tall;
// 富
- (BOOL)rich;
// 帅
- (BOOL)handsome;
@end


Person.m


// 1、10进制位赋值
//#define kTallMask 1
//#define kRichMask 2
//#define kHandsomeMask 4
// 2、2进制位赋值,0b:表示2进制位
//#define kTallMask 0b00000001
//#define kRichMask 0b00000010
//#define kHandsomeMask 0b00000100
// 3、运算符赋值
#define kTallMask (1<<0)
#define kRichMask (1<<1)
#define kHandsomeMask (1<<2)
@interface Person(){
  // char:长度一个字节(8个二进制位)
    char _tallRichHandsome;// 0b 0000 0000
}
@end
@implementation Person
- (void)setTall:(BOOL)tall {
    if (tall) {
      // 掩码,位或(|)运算
        _tallRichHandsome |= kTallMask;
    } else {
      //掩码取反,进行位与(&)运算
        _tallRichHandsome &= ~kTallMask;
    }
}
- (void)setRich:(BOOL)rich {
    if (rich) {
        _tallRichHandsome |= kRichMask;
    } else {
        _tallRichHandsome &= ~kRichMask;
    }
}
- (void)setHandsome:(BOOL)handsome {
    if (handsome) {
        _tallRichHandsome |= kHandsomeMask;
    } else {
        _tallRichHandsome &= ~kHandsomeMask;
    }
}
- (BOOL)tall {
  // 掩码,位与(&)运算:两次取反(!!)返回Bool类型
    return !!(_tallRichHandsome & kTallMask);
}
- (BOOL)rich {
    return !!(_tallRichHandsome & kRichMask);
}
- (BOOL)handsome {
    return !!(_tallRichHandsome & kHandsomeMask);
}
@end


main.m


#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Leader *person = [[Leader alloc] init];
        person.tall = YES;
        person.rich = YES;
        person.handsome = YES;
        NSLog(@"-------tall:%hhd",person.tall);
        NSLog(@"-------rich:%hhd",person.rich);
        NSLog(@"-------handsome:%hhd",person.handsome);
    }
    return 0;
}


2.2、结果分析

1、mask标记:表示掩码,一般用来位运算的。

2、掩码(mask)赋值有多种方式:


1、可以用10进制位赋值的。

2、用2进制位赋值的。

3、位移运算符赋值的,这里会用到左移运算符(<<)。

其中位移运算符赋值是最简单直观的,推荐使用这种方式。


3、掩码 + 位与(&)运算转Bool类型,需要两次取反(!!)。


三、结构体+位域实现


3.1、代码示例

#import <Foundation/Foundation.h>
@interface Student : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)tall;
- (BOOL)rich;
- (BOOL)handsome;
@end


#import "Student.h"
@interface Student(){
    struct {
        char tall : 1;
        char rich : 1;
        char handsome : 1;
    }_tallRichHandsome;
}
@end
@implementation Student
- (instancetype)init {
    if (self = [super init]) {
    }
    return self;
}
- (void)setTall:(BOOL)tall {
    _tallRichHandsome.tall = tall;
}
- (void)setRich:(BOOL)rich {
    _tallRichHandsome.rich = rich;
}
- (void)setHandsome:(BOOL)handsome {
    _tallRichHandsome.handsome = handsome;
}
- (BOOL)tall {
    return !!_tallRichHandsome.tall;
}
- (BOOL)rich {
    return !!_tallRichHandsome.rich;
}
- (BOOL)handsome {
    return !!_tallRichHandsome.handsome;
}


3.2、分析

当取值的时候,如果用如下方式实现:


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

发现,取值时打印出来为-1,实际二进制值为(0b1111 1111),这不是我们想要的结果,怎么处理?


/**
* tallRichHandsome.tall = 0b1                一个二进制位
*  BOOL                 = 0b0000 0000        一个字节为8个二进制位 
* ----------------------------------------------------------
*                   结果 = 0b1111 1111 = 255(-1)
* 分析:当一个二进制位赋值给一个8个二进制位时,前面不足的拿最左边值(1)的填补。
*/


那么这个问题这么处理呢?


1、取值时,因为拿到的是bool类型,可以两个取反获取到想要的值(如上面👆的例子)。


第一次取反,只要不等于0,返回的都是false,二次取反,得到我们需要的值true。


2、根据位于临近填补的特点,设置占位为两位(如:0b01),得到的就是:0b0000 0001


四、结构体+位域优化


根据上面的问题处理,这里只要修改如下部分代码:


@interface Student(){
    struct {// 结构体位域长度设置为2
        char tall : 2;
        char rich : 2;
        char handsome : 2;
    }_tallRichHandsome;
}
@end
// 取值不在需要二次取反
- (BOOL)tall {
    return _tallRichHandsome.tall;
}


缺点:

结构体位域存储方式,变量在结构体中的位置,直接决定了在二进制中的存储位置。如代码有增删改操作,容易定位不准确,导致出错。


五、共用体(推荐)


这是OC内部底层实现的逻辑。


5.1、代码示例

#import <Foundation/Foundation.h>
@interface Leader : NSObject
- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;
- (BOOL)tall;
- (BOOL)rich;
- (BOOL)handsome;
@end


#import "Leader.h"
#define kTallMask (1<<0)
#define kRichMask (1<<1)
#define kHandsomeMask (1<<2)
@interface Leader(){
    union {// 共用体:大家都共用一个字节
        char bits;//位
        struct {// 结构体仅是提高代码可读性,没有实际应用
            char tall : 1;
            char rich : 1;
            char handsome : 1;
        };
    }_tallRichHandsome;
}
@end
@implementation Leader
- (void)setTall:(BOOL)tall {
    if (tall) {
        _tallRichHandsome.bits |= kTallMask;
    } else {
        _tallRichHandsome.bits &= ~kTallMask;
    }
}
- (void)setRich:(BOOL)rich {
    if (rich) {
        _tallRichHandsome.bits |= kRichMask;
    } else {
        _tallRichHandsome.bits &= ~kRichMask;
    }
}
- (void)setHandsome:(BOOL)handsome {
    if (handsome) {
        _tallRichHandsome.bits |= kHandsomeMask;
    } else {
        _tallRichHandsome.bits &= ~kHandsomeMask;
    }
}
- (BOOL)tall {
    return !!(_tallRichHandsome.bits & kTallMask);
}
- (BOOL)rich {
    return !!(_tallRichHandsome.bits & kRichMask);
}
- (BOOL)handsome {
    return !!(_tallRichHandsome.bits & kHandsomeMask);
}
@end


5.2、分析

这是位运算与结构体的位域结合方式,利用各自的优势。

那有什么优势呢?


1、位运算:定位精准,提高运算效率。

2、结构体的位域:提高代码的可读性。


六、OC中的应用


1、ISA_MASK

1、在arm64架构以前,isa指针就是一个普通的指针,存储着Class、Meta-Class对象的内存地址。

2、从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域存储了更多信息。需要 &ISA_MASK 才能得到Class、Meta-Class对象的内存地址。


#   define ISA_MASK        0x0000000ffffffff8ULL

2、enum枚举

在OC中最常见的位运算是枚举设置


// 枚举设施孩子
typedef enum {
    ZMOptionsNone = 0,
    ZMOptionsOne = 1<<0,// 0b0001
    ZMOptionsTwo = 1<<1,// 0b0010
    ZMOptionsThree = 1<<2,// 0b0100
    ZMOptionsFour = 1<<3,// 0b1000
}ZMOptions;
// 取值
- (void)setOptions:(ZMOptions)options {
    if (options&ZMOptionsOne) {
        NSLog(@"ZMOptionsOne");
    }
    if (options&ZMOptionsTwo) {
        NSLog(@"ZMOptionsTwo");
    }
    if (options&ZMOptionsThree) {
        NSLog(@"ZMOptionsThree");
    }
    if (options&ZMOptionsFour) {
        NSLog(@"ZMOptionsFour");
    }
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // 赋值
    [self setOptions:ZMOptionsOne|ZMOptionsFour];
}
相关文章
|
7月前
|
运维 监控 网络协议
别再说 IPv6 只是“未来”了:我在生产环境踩过的那些坑
别再说 IPv6 只是“未来”了:我在生产环境踩过的那些坑
985 3
|
6月前
|
自然语言处理 安全
Stop Fixating on Prompts: Reasoning Hijacking and Constraint Tightening for Red-Teaming LLM Agents(论文解读)
本文提出的JailAgent框架,通过不修改用户提示词的隐式攻击方式,实现了对LLM智能体推理轨迹与记忆检索的高效劫持,兼具高攻击成功率、强泛化性、高隐蔽性与低计算开销,为LLM智能体的红队测试与安全评估提供了全新范式。
333 2
一个被很多主播忽略的细节:你的观众,正在静音看你直播
很多观众静音刷直播,却因听不到信息秒退。本文揭示这一被忽视的痛点,指出字幕是留住静音用户的关键:提升理解、适配口音/多语种、增强转化。推荐用衍播(OBSTrans)+OBS轻松实现双语实时字幕,低成本验证效果。(239字)
|
5月前
|
存储 人工智能 缓存
长上下文大模型的企业成本治理:从调用链到预算控制
Claude 1M 长上下文、Claude Code、MCP、Agent workflow 的讨论升温后,企业侧的关注点正在从“模型能力是否足够强”转向“能不能稳定、合规、可控地跑在业务里”。
324 2
|
消息中间件 安全 API
Apache RocketMQ ACL 2.0 全新升级
RocketMQ 作为一款流行的分布式消息中间件,被广泛应用于各种大型分布式系统和微服务中,承担着异步通信、系统解耦、削峰填谷和消息通知等重要的角色。随着技术的演进和业务规模的扩大,安全相关的挑战日益突出,消息系统的访问控制也变得尤为重要。然而,RocketMQ 现有的 ACL 1.0 版本已经无法满足未来的发展。因此,我们推出了 RocketMQ ACL 2.0 升级版,进一步提升 RocketMQ 数据的安全性。本文将介绍 RocketMQ ACL 2.0 的新特性、工作原理,以及相关的配置和实践。
14561 119
|
机器学习/深度学习 运维 算法
深入探索机器学习中的支持向量机(SVM)算法:原理、应用与Python代码示例全面解析
【8月更文挑战第6天】在机器学习领域,支持向量机(SVM)犹如璀璨明珠。它是一种强大的监督学习算法,在分类、回归及异常检测中表现出色。SVM通过在高维空间寻找最大间隔超平面来分隔不同类别的数据,提升模型泛化能力。为处理非线性问题,引入了核函数将数据映射到高维空间。SVM在文本分类、图像识别等多个领域有广泛应用,展现出高度灵活性和适应性。
902 2
|
网络虚拟化
网络实验 VlAN 中 Trunk Access端口的说明及实验
本文介绍了VLAN技术及其在网络实验中的应用,包括Access端口和Trunk端口的工作原理,以及如何通过实验划分不同VLAN实现内部通信和跨交换机实现VLAN间通信的详细步骤和配置。
网络实验 VlAN 中 Trunk Access端口的说明及实验
|
存储 数据采集 人工智能
信息系统框架标准TOGAF
信息系统框架标准TOGAF
1060 7
|
开发框架 IDE .NET
.NET 6 之 ABP vNext 初体验
本文将介绍在 .net6 平台的 asp.net core webapi 框架中,如何使用 abp vnext 框架进行模块化开发,重在思想理解。
1847 1
.NET 6 之 ABP vNext 初体验
|
API 文件存储
在钉钉的企业内部应用中
在钉钉的企业内部应用中
437 2

热门文章

最新文章