位运算的一些应用场景

/ 0评 / 0

1字节存储多个布尔

位运算方式

通过位运算,将多个 BOOL 数值存储到一个 char 类型变量里:

// 掩码(Mask),一般用于按位运算,用于取出或修改特定二进制位的值
#define A_MASK (1<<0) // 0b0001
#define B_MASK (1<<1) // 0b0010
#define C_MASK (1<<2) // 0b0100
#define D_MASK (1<<3) // 0b1000

@interface Person () {
    char _abcd; // 0b 0000 0000(规定存储位:0b 0000 dcba)
}
@end

@implementation Person
- (void)setA:(BOOL)a {
    if (a) {
        _abcd |= A_MASK;
    } else {
        _abcd &= ~A_MASK;
    }
}
- (void)setB:(BOOL)b {
    if (b) {
        _abcd |= B_MASK;
    } else {
        _abcd &= ~B_MASK;
    }
}
- (void)setC:(BOOL)c {
    if (c) {
        _abcd |= C_MASK;
    } else {
        _abcd &= ~C_MASK;
    }
}
- (void)setD:(BOOL)d {
    if (d) {
        _abcd |= D_MASK;
    } else {
        _abcd &= ~D_MASK;
    }
}

- (BOOL)a {
    return !!(_abcd & A_MASK);
}
- (BOOL)b {
    return !!(_abcd & B_MASK);
}
- (BOOL)c {
    return !!(_abcd & C_MASK);
}
- (BOOL)d {
    return !!(_abcd & D_MASK);
}
@end

位域方式

使用位域实现更方便,不过性能稍微低一点点:

@interface Person () {
    // 结构体支持位域,: 后面的数值表示占用二进制位数
    struct {
        char a : 1;
        char b : 1;
        char c : 1;
        char d : 1;
    } _abcd; // 0b 0000 dcba; 先写的在最右边
}
@end

@implementation Person
- (void)setA:(BOOL)a {
    _abcd.a = a;
}
- (void)setB:(BOOL)b {
    _abcd.b = b;
}
- (void)setC:(BOOL)c {
    _abcd.c = c;
}
- (void)setD:(BOOL)d {
    _abcd.d = d;
}

- (BOOL)a {
    return !!_abcd.a;
}
- (BOOL)b {
    return !!_abcd.b;
}
- (BOOL)c {
    return !!_abcd.c;
}
- (BOOL)d {
    return !!_abcd.d;
}
@end

位运算+位域方式

也是通过位运算实现,位域增加可读性,同时也保证了性能:

#define A_MASK (1<<0) // 0b0001
#define B_MASK (1<<1) // 0b0010
#define C_MASK (1<<2) // 0b0100
#define D_MASK (1<<3) // 0b1000

@interface Person () {
    // 共用体的所有成员,共用一块内存空间。也就是这个共用体也是1字节
    union {
        char bits; // 这个变量的大小要大于等于结构体的大小,才不会影响存储。比如结构体超过了8位,就不能用char类型,需要换更大的类型。

        // 这个结构体只是为了增加可读性,表示bits里存储的内容。
        struct {
            char a : 1;
            char b : 1;
            char c : 1;
            char d : 1;
        };
    } _abcd;
}
@end

@implementation Person
- (void)setA:(BOOL)a {
    if (a) {
        _abcd.bits |= A_MASK;
    } else {
        _abcd.bits &= ~A_MASK;
    }
}
- (void)setB:(BOOL)b {
    if (b) {
        _abcd.bits |= B_MASK;
    } else {
        _abcd.bits &= ~B_MASK;
    }
}
- (void)setC:(BOOL)c {
    if (c) {
        _abcd.bits |= C_MASK;
    } else {
        _abcd.bits &= ~C_MASK;
    }
}
- (void)setD:(BOOL)d {
    if (d) {
        _abcd.bits |= D_MASK;
    } else {
        _abcd.bits &= ~D_MASK;
    }
}

- (BOOL)a {
    return !!(_abcd.bits & A_MASK);
}
- (BOOL)b {
    return !!(_abcd.bits & B_MASK);
}
- (BOOL)c {
    return !!(_abcd.bits & C_MASK);
}
- (BOOL)d {
    return !!(_abcd.bits & D_MASK);
}
@end

掩码位数

上面都是用1个二进制位来存储,如果要使用多个二进制位,掩码的宏定义就需要修改了。

1个二进制位:

#define A_MASK (1<<0) // 0b0001
#define B_MASK (1<<1) // 0b0010
#define C_MASK (1<<2) // 0b0100
#define D_MASK (1<<3) // 0b1000

2个二进制位:

#define A_MASK (0b0011<<0) // 0b00000011
#define B_MASK (0b0011<<1) // 0b00000110
#define C_MASK (0b0011<<2) // 0b00001100
#define D_MASK (0b0011<<3) // 0b00011000

多个二进制位也是相同的原理,需要多少个二进制位,就有多少个二进制位为1

NS_OPTIONS选项

OC 通过 NS_OPTIONS 定义枚举选项,通过位运算实现多个枚举值的合并拆分:

// 0x01 | 0x02 == 0b0001 | 0b0010 == 0b0011
NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;

// 0b0011 & 0b0001 == 0b0001
if (options & NSKeyValueObservingOptionNew) {
    NSLog(@"NSKeyValueObservingOptionNew");
}

// 0b0011 & 0b0010 == 0b0010
if (options & NSKeyValueObservingOptionOld) {
    NSLog(@"NSKeyValueObservingOptionOld");
}

// 0b0011 & 0b0100 != 0b0000
if (options & NSKeyValueObservingOptionInitial) {
    NSLog(@"NSKeyValueObservingOptionInitial");
}

// 0b0011 & 0b1000 != 0b0000
if (options & NSKeyValueObservingOptionPrior) {
    NSLog(@"NSKeyValueObservingOptionPrior");
}

// 增加选项
// 0b0011 | 0b0100 = 0b0111
options |= NSKeyValueObservingOptionInitial;

// 0b0111 & 0b0100 == 0b0100
if (options & NSKeyValueObservingOptionInitial) {
    NSLog(@"增加后-NSKeyValueObservingOptionInitial");
}

// 减少选项
// 0b0111 & (~0b0100) = 0b0111 & 0b1011 = 0b0011
options &= ~NSKeyValueObservingOptionInitial;

// 0b0011 & 0b0100 != 0b0000
if (options & NSKeyValueObservingOptionInitial) {
    NSLog(@"减少后-NSKeyValueObservingOptionInitial");
}

/**
 最终打印
 NSKeyValueObservingOptionNew
 NSKeyValueObservingOptionOld
 增加后-NSKeyValueObservingOptionInitial
 */

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注