一、前言
位運算在我們實際開發中用得很少,主要原因還是它對於我們而言不好讀、不好懂、也不好計算,如果不經常實踐,很容易就生疏了。但實際上,位運算是一種很好的運算思想,它的優點自然是計算快,代碼更少。
二、基本知識介紹
- 二進制:
二進制是由1和0兩個數字組成的,它可以表示兩種狀態,即開和關。所有輸入電腦的任何信息最終都要轉化為二進制。目前通用的是ASCII碼。最基本的單位為bit。 - 位運算:
程序中的所有數在計算機內存中都是以二進制的形式儲存的。位運算說穿了,就是直接對整數在內存中的二進制位進行操作。比如,and運算本來是一個邏輯運算符,但整數與整數之間也可以進行and運算。舉個例子,6的二進制是110,11的二進制是1011,那么6 and 11的結果就是2,它是二進制對應位進行邏輯運算的結果(0表示False,1表示True,空位都當0處理)。
三、問題引用
- 老鼠試毒
有1000瓶水,其中有一瓶有毒,小白鼠只要嘗一點帶毒的水24小時后就會死亡,問至少要多少只小白鼠才能在24小時內鑒別出哪瓶水有毒?

這里,位掩碼的使用就可以巧妙的解決此問題。
我們先將問題簡化一下:假設只有8瓶水,其中1瓶有毒。

將該矩陣轉置,得:

依上述場景,取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