一、前言

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

二、基本知识介绍

  • 二进制:
    二进制是由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 = << ,//0001,1
permission2 = << ,//0010,2
permission3 = << ,//0100,4
permission4 = << ,//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 = << ,//0001,1
ALLOW_INSERT = << ,//0010,2
ALLOW_UPDATE = << ,//0100,4
ALLOW_DELETE = << ,//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) == ;
}
/** 是否仅仅拥有某些权限 */
- (BOOL)isOnlyAllow:(OptionPermission)permission {
return self.flag == permission;
}

五、写在最后

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

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

位掩码(BitMask)的介绍与使用的更多相关文章

  1. Java中的位掩码BitMask

    目录 JDK源码的使用 日常工作中的使用 JDK源码的使用 最近在JDK源码中闲逛,无意中看到了java.lang.reflect.Modifier这个类,这个类很简单,都是些常量定义和判断方法,于是 ...

  2. Java位运算在程序设计中的使用:位掩码(BitMask)

    在Java中,位运算符有很多,例如与(&).非(~).或(|).异或(^).移位(<<和>>)等.这些运算符在日常编码中很少会用到. 在下面的一个例子中,会用到位掩码( ...

  3. Windows API 第16篇 GetLogicalDrivers 获取驱动器位掩码

    函数原型:DWORD GetLogicalDrives(VOID);The GetLogicalDrives function retrieves a bitmask representing the ...

  4. js 位掩码

    原文 定义掩码 const mask0 = parseInt("00000001", 2); const mask1 = parseInt("00000010" ...

  5. Rails Migration Data Model栏位修改及数据类型介绍

    测试版本Ruby:2.3.1   Rails:5.0.1 一.增加栏位       给devise默认的用户新增增加username字段 $ rails generate migration add_ ...

  6. Codeforces Round #626 Div2 D. Present(位掩码,二分)

    题目链接:https://codeforces.com/contest/1323/problem/D 题意:给了大小为4e5的数组a,其中1<=ai<=1e7.求所有点对和的异或和,即: ...

  7. (转载)中文Appium API 文档

    该文档是Testerhome官方翻译的源地址:https://github.com/appium/appium/tree/master/docs/cn官方网站上的:http://appium.io/s ...

  8. 【MarkMark学习笔记学习笔记】javascript/js 学习笔记

    1.0, 概述.JavaScript是ECMAScript的实现之一 2.0,在HTML中使用JavaScript. 2.1 3.0,基本概念 3.1,ECMAScript中的一切(变量,函数名,操作 ...

  9. 中文Appium API 文档

    该文档是Testerhome官方翻译的源地址:https://github.com/appium/appium/tree/master/docs/cn官方网站上的:http://appium.io/s ...

随机推荐

  1. 配置Web.config 元素CustomErrors

    一.customErrors 元素 属性 说明 defaultRedirect 指定出错时将浏览器定向到的默认 URL.如果未指定该属性,则显示一般性错误. 可选的属性. URL 可以是绝对的(如 w ...

  2. kubernetes job的原理

    job例子: apiVersion: batch/v1 #job的apiVersion kind: Job #资源类型为job metadata: labels: name: busybox name ...

  3. BZOJ4477[Jsoi2015]字符串树——可持久化trie树

    题目描述 萌萌买了一颗字符串树的种子,春天种下去以后夏天就能长出一棵很大的字符串树.字符串树很奇特,树枝上都密密麻麻写满了字符串,看上去很复杂的样子.[问题描述]字符串树本质上还是一棵树,即N个节点N ...

  4. Codeforces Round #415 Div. 1

    A:考虑每对最大值最小值的贡献即可. #include<iostream> #include<cstdio> #include<cmath> #include< ...

  5. Matplotlib学习---用matplotlib画雷达图(radar chart)

    雷达图常用于对多项指标的全面分析.例如:HR想要比较两个应聘者的综合素质,用雷达图分别画出来,就可以进行直观的比较. 用Matplotlib画雷达图需要使用极坐标体系,可点击此链接,查看对极坐标体系的 ...

  6. 【XSY2665】没有上司的舞会 LCT DP

    题目大意 有一棵树,最开始只有一个点.每次会往这棵树中加一个点,总共\(n\)次.输出每次加点后树的最大独立集大小. 强制在线. \(n\leq 300000\) 题解 显然是LCT. 那么要维护什么 ...

  7. Java8的Stream语法详解(转载)

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

  8. 【POJ1037】A decorative fence(DP)

    BUPT2017 wintertraining(15) #6C 题意 给长度n的数列,1,2,..,n,按依次递增递减排序,求字典序第k小的排列. 题解 dp. up[i][j]表示长度为j,以第i小 ...

  9. LOJ #2234. 「JLOI2014」聪明的燕姿(搜索 + 数论)

    题意 给出一个数 \(S\) ,输出所有约数和等于 \(S\) 的数. \(S \le 2 \times 10^9\) ,数据组数 \(\le 100\) . 题解 首先用约数和定理: \[ \beg ...

  10. 「POJ3696」The Luckiest number【数论,欧拉函数】

    # 题解 一道数论欧拉函数和欧拉定理的入门好题. 虽然我提交的时候POJ炸掉了,但是在hdu里面A掉了,应该是一样的吧. 首先我们需要求的这个数一定可以表示成\(\frac{(10^x-1)}{9}\ ...