• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Runtime系列位运算在OC的取值和赋值01

武飞扬头像
瓜子三百克
帮助1

学新通

✅作者简介:大家好我是瓜子三百克,一个非科班出身的技术程序员,还是喜欢在学习和开发中记录笔记的小白博主!
📃个人主页:瓜子三百克的主页
🔥系列专栏:OC语法
💖如果觉得博主的文章还不错的话,请点赞👍 收藏⭐️ 留言📝支持一下博主哦🤞

学新通


本片文章介绍将数据(如: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];
}

**🏆结束语🏆 **

最后如果觉得我写的文章对您有帮助的话,欢迎点赞✌,收藏✌,加关注✌哦,谢谢谢谢!!

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgabbea
系列文章
更多 icon
同类精品
更多 icon
继续加载