iOS小知识之联合体/位域

432 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

1.位域

信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如:“真/假”值,用0或1比啊是,只需1位即可 位域(Bit field):为一种数据结构,可以把数据以位的形式紧凑的存储,并允许程序员对此结构的位进行操作。 使用位域的优势:

  • 可以使数据单元节省存储空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要
  • 位于可以很方便的访问一个整数值的部分内容从而可以简化程序源代码

案例:

struct LGCar1 { 
    BOOL front; 
    BOOL back; 
    BOOL left; 
    BOOL right; 
};
  • LGCar1结构体中,包含前后左右四个BOOL类型的成员变量
struct LGCar1 car1; 
NSLog(@"LGCar1:%ld",sizeof(car1));
------------------------- 
LGCar1:4
  • 占4字节,即32位 四个”真/假“值分配4字节,实属空间浪费。正常来说,使用4位就可以表示,占1字节即可 即“0000 1111”,使用低4位,分别表示前、后、左、右 所以可以使用如下代码,使用位域节省空间
struct LGCar2 { 
    BOOL front : 1;
    BOOL back : 1; 
    BOOL left : 1; 
    BOOL right : 1; 
};
  • 语法:在成员变量后面增加: x,表示其所占位数
struct LGCar2 car2; 
NSLog(@"LGCar2:%ld",sizeof(car2)); 
------------------------- 
LGCar2:1

运行发现,仅占1字节,4位就够用了

2. 联合体

结构体(struct):所有变量是“共存”的

  • 优点:包容性强,成员之间不会相互影响
  • 缺点:内存空间的浪费,不管用不用,全分配
struct LGTeacher1 { 
    char *name; 
    int age;
};
  • 定义结构体,包含name和age两个成员变量
struct LGTeacher1 teacher1;
teacher1.name = "Cooci";
teacher1.age = 18; 
NSLog(@"name:%s,age:%i,sizeof:%ld", teacher1.name, teacher1.age, sizeof(teacher1));
------------------------- 
name:Cooci,age:18,sizeof:16
  • 成员变量之间不会相互影响,占16字节

联合体(union):成员之间内存共用,各变量是“互斥”的

  • 优点:节省内存空间,内存的使用更为精细灵活
  • 缺点:包容性较差 如以下联合体:
union LGTeacher2 {
    char *name; 
    int age; 
};
  • 定义联合体,包含name和age两个成员变量
struct LGTeacher1 teacher1;
teacher1.age = 18; 
teacher1.name = "Cooci"; 
NSLog(@"name:%s,age:%i,sizeof:%ld", teacher1.name, teacher1.age, sizeof(teacher1)); 
------------------------- 
name:Cooci,age:15934,sizeof:8

可以发现

  • 成员之间内存共用,只有一个成员变量有值。由于name赋值,导致age变为坏地址
  • 联合体的大小,为最大成员变量的大小
  • 联合体一般会配合位域一起使用

3. 项目中的使用

打开LGCar.m文件,写入以下代码: 宏定义

#import "LGCar.h" 

#define LGDirectionFrontMask (1 << 0) 
#define LGDirectionBackMask (1 << 1) 
#define LGDirectionLeftMask (1 << 2) 
#define LGDirectionRightMask (1 << 3)

定义联合体+位域

@interface LGCar() {
    union { 
        char bits; 
        struct { 
            char front : 1;
            char back : 1; 
            char left : 1; 
            char right : 1; 
        };
    }_direction; 
} 

@end

初始化

- (instancetype)init { 
    self = [super init];
    if (self) {
        _direction.bits = 0b0000000000;
    }
    return self; 
}

front的getter/setter方法

- (void)setFront:(BOOL)isFront { 
    if (isFront) { 
        _direction.bits |= LGDirectionFrontMask; 
    } else {
        _direction.bits |= ~LGDirectionFrontMask; 
    } 
    NSLog(@"%s",__func__); 
}
- (BOOL)isFront{ 
    return _direction.front;
}