zoukankan      html  css  js  c++  java
  • 位掩码(BitMask)的介绍与使用

    一、前言

    位运算在我们实际开发中用得很少,主要原因还是它对于我们而言不好读、不好懂、也不好计算,如果不经常实践,很容易就生疏了。但实际上,位运算是一种很好的运算思想,它的优点自然是计算快,代码更少。

    二、基本知识介绍

    • 二进制:
      二进制是由1和0两个数字组成的,它可以表示两种状态,即开和关。所有输入电脑的任何信息最终都要转化为二进制。目前通用的是ASCII码。最基本的单位为bit。
    • 位运算:
      程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

    三、问题引用

    • 老鼠试毒
      有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水24小时后就会死亡,问至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?
     
    老鼠试毒.png

    这里,位掩码的使用就可以巧妙的解决此问题。

    我们先将问题简化一下:假设只有8瓶水,其中1瓶有毒。

     
    8杯水分别编号.png

    将该矩阵转置,得:

     
    水杯矩阵转置.png

    依上述场景,取4只容器,转置后的矩阵数列配组合溶液:
    取数位上为1的水,放入相应的容器,即:
    第一杯:只包含8号水
    第二杯:包含4、5、6、7号水
    第三杯:包含2、3、6、7号水
    第四杯:包含1、3、5、7号水

    取4只老鼠,编号1、2、3、4,分别喝下第一杯...第四杯水,
    4只老鼠的生死状态依次记为 w x y z,(w,x,y,z = {0,1})
    死亡记作1,非死亡记作0
    将二进制数列wxyz转为十进制,则得到有毒水的号码。
    假设6号水有毒,那么往回推算,不难看出,第2、3只老鼠会死亡,
    得到的wxyz的数列就是0110,转十进制后就是6。

    将1000瓶依次编号:1,2,3,4,...,1000; 且都记作二进制;
    那我们要用多少位来表示呢?
    总数是1000,2^9=512, 2^10=1024,于是至少要10位才够表示,
    也就是:0000000001,0000000010,0000000011,...,1111101000;
    道理同上。

    四、结合实际问题

    我们已经见识了二进制的厉害之处了,接下来我们结合代码来看看,在iOS开发中的应用(其实在任何开发中都一样)

    • 在实际开发中,我们常常遇到权限的判断的问题,比如说,不同的用户对系统有不同的操作权限,有的用户可能有多种权限,我们最常规的办法就是每一个权限定义一个BOOL值。
      假设,某系统有4种权限,那么,就有了:
    @interface BM_User : NSObject
    @property (nonatomic, assign) BOOL permission1;
    @property (nonatomic, assign) BOOL permission2;
    @property (nonatomic, assign) BOOL permission3;
    @property (nonatomic, assign) BOOL permission4;
    @end

    那用户A同时拥有permission1、permission2、permission4怎么表示呢?

        BM_User *userA = [[BM_User alloc] init];
        userA.permission1 = YES;
        userA.permission2 = YES;
        userA.permission4 = YES;

    这样的操作大家见多了吧?那我们来看看另一种写法:

    @interface BM_User : NSObject
    @property (nonatomic, assign) OptionPermission permission;
    @end

    有人就要问了,OptionPermission是什么鬼?来,继续。。。

    /**
     权限枚举
    
     - 1: permission1,二进制第1位,0表示否,1表示是
     - 2: permission2,二进制第2位,0表示否,1表示是
     - 4: permission3,二进制第3位,0表示否,1表示是
     - 8: permission4,二进制第4位,0表示否,1表示是
     */
    typedef NS_OPTIONS(NSUInteger, OptionPermission) {
        permission1 = 1 << 0,//0001,1
        permission2 = 1 << 1,//0010,2
        permission3 = 1 << 2,//0100,4
        permission4 = 1 << 3,//1000,8
    };

    那用户A同时拥有permission1、permission2、permission4怎么表示呢?

        BM_User *userA = [[BM_User alloc] init];
        userA.permission = permission1 | permission2 | permission4;

    是不是神清气爽?

    现在我们就具体化4种权限,并给出基础位掩码的表达及运算:

    #ifndef BM_Head_h
    #define BM_Head_h
    /**
     权限枚举
     - 1: 是否允许查询,二进制第1位,0表示否,1表示是
     - 2: 是否允许新增,二进制第2位,0表示否,1表示是
     - 4: 是否允许修改,二进制第3位,0表示否,1表示是
     - 8: 是否允许删除,二进制第4位,0表示否,1表示是
     */
    typedef NS_OPTIONS(NSUInteger, OptionPermission) {
        ALLOW_SELECT = 1 << 0,//0001,1
        ALLOW_INSERT = 1 << 1,//0010,2
        ALLOW_UPDATE = 1 << 2,//0100,4
        ALLOW_DELETE = 1 << 3,//1000,8
    };
    #endif /* BM_Head_h */
    #import "BM_Permission.h"
    #import "BM_Head.h"
    @interface BM_Permission ()
    /** 存储目前的权限状态 */
    @property (nonatomic, assign) OptionPermission flag;
    @end
    @implementation BM_Permission
    /** 重新设置权限 */
    - (void)setPermission:(OptionPermission)permission {
        self.flag = permission;
    }
    /** 添加一项或多项权限 */
    - (void)enable:(OptionPermission)permission {
        self.flag |= permission;
    }
    /** 删除一项或多项权限 */
    - (void)disable:(OptionPermission)permission {
        self.flag &= ~permission;
    }
    /** 是否拥某些权限 */
    - (BOOL)siAllow:(OptionPermission)permission {
        return (self.flag & permission) == permission;
    }
    /** 是否禁用了某些权限 */
    - (BOOL)isNotAllow:(OptionPermission)permission {
        return (self.flag & permission) == 0;
    }
    /** 是否仅仅拥有某些权限 */
    - (BOOL)isOnlyAllow:(OptionPermission)permission {
        return self.flag == permission;
    }

    五、写在最后

    • 大家还可以自行搜索一下NS_OPTIONS与NS_ENUM的区别,他们都是用来定义枚举的,但其用法是有很大不同。
    • 博主我最近一直在考虑优化代码,正在开发的项目中就有很多权限判断的问题,我也在寻找各种各样更好的写法。
    • 也希望大家重视代码的表达,因此更加优化自己的代码。

    作者:Leewins
    链接:https://www.jianshu.com/p/4e73512c03b8

  • 相关阅读:
    [Swift]关键字:class与staitc的区别
    [Swift]LeetCode1171. 从链表中删去总和值为零的连续节点 | Remove Zero Sum Consecutive Nodes from Linked List
    [Swift]LeetCode1172. 餐盘栈 | Dinner Plate Stacks
    [Swift]LeetCode1170. 比较字符串最小字母出现频次 | Compare Strings by Frequency of the Smallest Character
    [Swift]LeetCode1169. 查询无效交易 | Invalid Transactions
    [Swift]LeetCode1167. 连接棒材的最低费用 | Minimum Cost to Connect Sticks
    [Swift]LeetCode1166.设计文件系统 | Design File System
    [Swift]LeetCode1165. 单行键盘 | Single-Row Keyboard
    [Swift]LeetCode1168. 水资源分配优化 | Optimize Water Distribution in a Village
    METRO风格
  • 原文地址:https://www.cnblogs.com/zhaoshujie/p/9797802.html
Copyright © 2011-2022 走看看