国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

位運算世界暢游指南

番茄西紅柿 / 1307人閱讀

摘要:另外值得一提的是,大部分語言的位運算符都相同,所以這是一篇老少皆宜的文章。基本位運算符位運算符就像是控制比特位的扳手,在學習位運算前先介紹下每個運算符的意義及其用法。按位與運算規則只有在兩個值都為時結果才為,兩個值中有一個為結果便為。

目錄

概念介紹

基本位運算符

按位與 ( AND )

按位或 ( OR )

按位異或 ( XOR )

取反 ( NOT )

右移 ( >> )

左移 ( << )

基礎技巧

將某些二進制位設置為1

掩碼

取出第i位二進制值

計算無符號變量的取值范圍

奇偶判斷

統計二進制中1個數

交換兩個變量的值(無臨時變量)

子集生成

進階技巧

getBits

setBits

Objective-C的Runtime中的位運算應用

判斷是否是TaggedPointer

isa_t

方法緩存中的Hash函數

NS_OPTIONS

參考資料

概念介紹

我們都知道,計算機中的所有數據最終都是以二進制(bit)的形式存儲在計算機的。而在我們平時開發中所接觸數據的大多是字節為單位的,有了位運算之后我們就可以操作字節中的比特位了。在iOS的runtime源碼以及NS_OPTIONS 中都運用了位運算的知識,可見其重要性了。另外值得一提的是,大部分語言的位運算符都相同,所以這是一篇老少皆宜的文章。

閱讀本篇文章前,你需要知道的一些東西:

    我們在討論二進制的時候,位序一般都是從右(低位)到左(高位)的。舉個例子,對于二進制0011來說,第0位是1,第1位是1,第二位是0,第三位是0。

    在大部分編程語言中,0x開頭代表十六進制,比如0xff代表十六進制ff;0b開頭代表二進制,比如0b11111111代表二進制的11111111;0開頭代表8進制,比如0377代表8進制377;如果你不寫前綴,那默認就是10進制。無論你是幾進制,你所描述的本質其實是一樣的,只是表現形式不同而已。比如前面的0xff、0b11111111、0377、255,它們都是等價的。你可以在編程語言中語言測試下。

  bool c  = 0xff == 0b11111111; // true
  bool c0 = 0b11111111 == 0377; // true
  bool c1 = 0377 == 255; //true

    之所以程序員都喜歡用16進制,首先是因為16進制和二進制的轉換實在太方便了。比如0xFAFA,直把每個字母轉成4位二進制拼接在一起即可,0xF:0b1111 ,0xA:0b1010,所以0xFAFA二進制是0b1111101011111010。另外一點,因為16進制中2個字母代表8位二進制(一個字節),所以當我們看到16進制的時候就能立馬知道多少個字節了。比如前面的0xFAFA,第一個字節是0xFA,第二個字節也是0xFA,共兩個字節。

基本位運算符

位運算符就像是控制比特位的扳手,在學習位運算前先介紹下每個運算符的意義及其用法。

按位與 ( AND )

運算規則:只有在兩個值都為1時結果才為1,兩個值中有一個為0結果便為0。在編程語言里一般用 & 表示與運算符。


舉個例子,10100001 & 10001001 = 10000001。(注:操作數都為二進制。)

按位或 ( OR )

運算規則: 兩個值中有一個為1結果便為1,兩個值都為0時結果才為0。在編程語言里一般用 | 表示或運算符。


舉個例子,10100001 | 10001001 = 10101001。


按位異或 ( XOR )

運算規則: 只有當兩個值不相同時結果才為1,兩個值相同時結果為0。在編程語言里一般用 ^ 表示異或運算符。


舉個例子,10100001 ^ 10001001 = 00101000。

取反 ( NOT )

在數值的二進制表示方式上,將0變為1,將1變為0。在編程語言里一般用 ~ 表示取反運算符。
來看一個例子可能會更加直觀:

右移 ( >> )

右移將操作數的二進制位整體向右移動N位,空出部分用0填充,在編程語言里一般用 >>表示右移運算符。

舉個例子,下圖對二進制 10111101 右移3位后,最右邊的101被移除,左邊空出來3位用0填充(本文章默認所有數據都為無符號類型,所以本操作為邏輯右移)。


左移 ( << )

左移將操作數的二進制位整體向左移動N位,空出部分用0填充,在編程語言里一般用 << 表示左移運算符。

舉個例子,下圖對二進制 10111101 左移4位后,最左邊的1011被移除,右邊空出來4位用0填充。

基礎技巧

這里先介紹一些比較簡單實用的位運算技巧,這些技巧在日常開發中也是比較常用的。

將某些二進制位設置為1

假設x=0b10011010,現在我想將第5、6位置為1,將其變為0b11111010,那么執行 (x = x | 0b01100000) 就是我們想要的結果;那若是想將第0、5、6為置為1,變成0b11111011呢?那么執行(x = x | 0b01100001)就是我們想要的結果。 根據上面的兩個例子,我們可以得到一個結論:

x = x | SET_ON ,該語句將x中對應于SET_ON中為1的二進制位設置為1;x中對應于SET_ON中為0的二進制位保持不變。

掩碼

掩碼這個詞經常能在計算機網絡里聽到,比較熟悉的話就是子網掩碼了。掩碼是起的非常好的一個名字,當我們的操作數和掩碼進行與運算(&)后,掩碼中二進制為0的位會屏蔽掉原操作數對應的二進制位。 舉個例子,假如現在我有一個2個字節的數據0xBA15,若要屏蔽掉中間0xA1這8位二進制變成0xB005,該如何設計掩碼呢?答案很簡單,只要將掩碼中間8位設為0其他設為1即可,所以本例中的掩碼應為0xF00F,0xBA15 & 0xF00F=0xB005。可以結合下圖理解:


取出第i位二進制值

這個函數傳入一個data,返回其二進制從右邊開始數第i位的值。

unsigned char getBit( unsigned long data , int i  ) {

    // i = 0時,代表取最右邊的哪一位。
    data  = data >> i ;
    return data & 1 ;
}

原理很簡單,先將data右移i位,這樣能保證第i位的值處于data的最右邊,然后再用data & 1取出即可。 舉個例子,如果我調用了{getBit(168,3)},168對應的二進制為10101000,右移3位后變成00010101,最后00010101 & 00000001 = 1,取出成功。

計算無符號變量的取值范圍

筆者在mac上的unsigned long 是8個字節,可以存儲64位二進制,由于沒有符號位,故只需將這64位二進制都填充為1就得到unsigned long變量的最大值了。

  // 將全0取反變為全1裝進變量x中。
   unsigned long x = ~0;
   // 輸出二進制為全1的變量x
   printf("unsigned long max = %lu
",x);

奇偶判斷

如果最后一位二進制為0,那么這個數字就是偶數,如果為1就是奇數。這里給出實現函數:

int isOdd(int value) {
    return (value & 1); // 取出最后一位二進制,若為1則是奇數
}

說下大致原理:若最后一位二進制為0,那么二進制轉成十進制后必然可以寫成2n的形式,必為偶數。比如我隨便寫一個最后一位為0的二進制數字 10001010,那么其十進制數為2+2^3+2^7 = 2*(1+2^2+2^6),故為偶數,大家可以多寫幾組數字驗證。

關于負數:雖然負數在計算機中以補碼的方式存儲,但由于補碼最后一位和原碼最后一位相同,所以上面的函數同樣適用于負數。為什么呢?舉個例子:

假如最后一位是0,取反后變成1,然后再+1又變成0。

假如最后一位是1,取反后變成0,然后再+1又變成1。

看到了吧,最后又變回去了。

統計二進制中1個數
int x =0xba;//10111010
int count = 0;
while (x!=0) {
    x = x&(x-1);
    count++;
}
printf("%d
",count);

循環中每次執行x = x&(x-1)后,x的二進制的最后一個1就會被消去。當所有1都被消去后,count計數完畢,x=0,退出循環。

那么為什么x = x&(x-1)能夠消去其二進制的最后一個1呢?舉個例子:

假如x=101000,那么 x-1=100111。

假如x=101011,那么 x-1=101010。

可以發現規律:

當x=nn..nn100..00這種形式時,x-1=nn..nn011..11。 這個時候x & (x-1) = nn..nn100..00 & nn..nn011..11 = nn..nn000.00,x最后一個1被消去。

當x=nn..nn1這種形式時,x-1=nn..nn0。 這個時候x & (x-1) = nn..nn1 & nn..nn0 = nn..nn0,x最后一個1被消去。

交換兩個變量的值(無臨時變量)
// 注:參數是c++的引用類型。
void swap(int &a,int &b) {
    a=a^b;
    b=a^b;
    a=a^b;
}

想要了解原理,需要先知道幾個異或運算的性質:

交換律:a^b = b^a

結合律:(a^b)^c = a^(b^c)

恒等律:a^0 = a

規零律:a^a = 0

自反性:a^b^b = a

假設一開始,a=k,b=t

執行a=a^ba=k^t;

執行b=a^bb = k^t^t=k,注意這里用到了自反性;

執行a=a^ba=k^t^k=t^k^k=t,注意這里用到了交換律和自反性;

最后得到a=t,b=k,交換完成。

當然了,不僅限于交換整型變量。舉一個不太常用的例子,我們可以不用臨時變量交換兩個c語言字符串。下面代碼中的a和b本質上是在交換"a-a-a-a-a-a"和"b-b-b-b-b-b"地址,所以效果也是一樣的。

 char *a = "a-a-a-a-a-a";// 存儲在數據區的字符串常量
 char *b = "b-b-b-b-b-b";//存儲在數據區的字符串常量
 printf("before exchange: a=%s,b=%s
",a,b);
 a = (char*)((long)a^(long)b);
 b = (char*)((long)a^(long)b);
 a = (char*)((long)a^(long)b);
 printf("after exchange: a=%s,b=%s
",a,b);
 /*
         最終輸出為:
          /*
         最終輸出為:
         before exchange: a=a-a-a-a-a-a,b=b-b-b-b-b-b
         after exchange: a=b-b-b-b-b-b,b=a-a-a-a-a-a
 */

子集生成

假設現在有一集合A={a,b,c},要求生成這個集合的所有子集。構造子集時,我們可以使用二進制中第i位的值決定是否要選取集合A中的第i個元素。其中值為1代表選取,值為0代表不選取。舉個例子,100代表只選第一個元素a,其構成的子集為{a};101代表選取第一個a以及第三個c,其構成的子集為{a,c}。

下面列舉出A的所有子集:

編號 A的子集 人類思考過程 二進制表示
0 {} 什么都不選 000
1 {c} 不選a,不選b,選c 001
2 {b} 不選a,選b,不選c 010
3 {b,c} 不選a,選b,選c 011
4 {a} 選a,不選b,不選c 100
5 {a,c} 選a,不選b,選c 101
6 {a,b} 選a,選b,不選c 110
7 {a,b,c} 選a,選b,選c 111

細心的話,應該能發現上面的表格中的編號和二進制剛剛好能對的上。所以對于有個n個元素的集合,只要生成0到2^n-1個整數編號,然后根據每個編號對應的二進制解析出相應的子集即可。 下面是c語言實現的代碼:

#include 
#include 
void prinstSubSet(char *S,int id) {
    int n = (int)strlen(S);// 集合的元素個數
    char result[100];
    int index=0;
    printf("{");
    for(int i=n-1;i>=0;i--){
        if ((id>>i)&1) {
            // 若第i位值為1,代表選擇第i個元素(從右邊開始數)
            result[index++]=S[n-1-i];//由于字符串第0個字符是最左邊,所以要顛倒下。
        }
    }
    for(int i =0;iprintf("%c",result[i]);
        if(i!=index-1)
            printf(",");
    }
    printf("}
");
}
void create(char *S) {
    int n = (int)strlen(S);// 集合的元素個數
    int begin = 0;
    int end = (1<-1; // 2^n-1
    
    //生成0到2^n-1個編號(id)
    for (int id = begin;id<=end;id++) {
        prinstSubSet(S, id);// 根據編號對應的二進制輸出子集
    }
}
int main(int argc, const char * argv[]) {
    create("abc");// 生成{a,b,c}的子集
    return 0;
}

進階技巧

這里介紹C語言程序設計這本書中的兩個非常實用的函數,相信你在平時的項目中也能應用的到。

getBits

該函數用來返回x中從右邊數第p位開始向右數n位二進制。

unsigned getBits(unsigned x,int p,int n) {
    return (x>>(p+1-n)) & ~(~0<

舉個例子,調用getBits(168,5,3)后,返回168對應二進制10101000從右邊數第5位開始向右3位二進制,也就是101。可以結合下圖理解:

下面來說以下原理:

一開始執行(x>>(p+1-n)) 這里是為了將期望獲得的字段移動到最右邊。用上面的例子,執行完后x變成:


~(~0< 是為了生成右邊n位全1的掩碼。 對于上面的例子~(~0<<3) ,我們一起來分析下過程。

    一開始執行~0生成全1的二進制,11111111。

    然后左移3位變成11111000。

    最后執行圓括號左邊的~,取反變成00000111,現在掩碼生成完成。


最后執行中間的那個&,將(x>>(p+1-n))~(~0<與運算,取出期望字段。對于上面的例子,對應過程圖如下:

setBits

該函數返回x中從第p位開始的n個(二進制)位設置為y中最右邊n位的值,x的其余各位保持不變。

unsigned setBits(unsigned x, int p,int n , unsigned y) {
    return (  x & ~( ~( ~0 << n ) << ( p+1-n ) ) ) |
           (y & ~(~0 << n) ) << (p+1-n);
}

舉個例子(#2),調用setbits(168, 5, 4, 0b0101)后,將168對應二進制10101000從右邊數第5位開始的4個二進制位設置為0101,設置完后變成10010100,最后將結果返回。可以結合下圖理解:

第一眼看到這個函數代碼還是有一些恐怖的,不用害怕,我們一層層解析,相信你一定能感受位運算的精妙之處!

我們先要將函數拆成兩個部分看待,第一部分是( x & ~( ~( ~0 << n ) << ( p+1-n ) ) )記為$0;另一部分是(y & ~(~0 << n) ) << (p+1-n)記為$1。 下面分析下$0和$1的作用:

其中,$0是為了將x中期望修改的n個二進制清0。在例子(#2)中,$0返回的二進制應該為:10000000,注意到紅體部分已經被清0。

$1是為了取出y最右邊n個二進制,并與x中待修改的那n個二進制對齊。在例子(#2)中,$1返回的二進制應該:00010100。

最后$0 | $1 ,也就是將$1的值設置到$0當中。在在例子(#2)中,$0 | $1 = 10000000 | 00010100 = 10010100,設置完成。

下面具體分析下$0是如何將期望修改的n個二進制清0的:

既然是清0,我們可以想使用到最早所學的掩碼,所以可以將$0以&為分割符拆成兩段看待,其中~( ~( ~0 << n ) << ( p+1-n ) )生成x清0所需要的掩碼。

一開始執行 ~(~0 << n) 生成右邊n個1,其余全為0的。代入例子(#2)的參數,也就是~(~0 << 4),結果為:00001111。這里為了方便記憶,把~(~0 << n)記為$$0 。

然后接著執行$$0 << (p+1-n),將右邊n個1左移到相應位置上。代入例子(#2)的參數及上一步的結果,應執行00001111 << (5+1-4),結果為00111100。這里將$$0 << (p+1-n)記為$$1。

最后執行最外層~$$1,生成清零所需的掩碼。代入例子(#2)的參數及上一步的結果,應執行~00111100 ,結果為11000011,掩碼生成完畢。

最后執行 x & ~$$1,用掩碼將x中待清零的位清0。代入例子(#2)的參數及上一步的結果,應執行10101000 & 11000011結果為10000000,清0成功。

下面具體分析下$1是如何取出y最右邊n個二進制并與x中待修改的那n個二進制對齊的:

首先 ~(~0 << n)和$0第一個步驟一樣,不過這次直接用這個結果當作掩碼。代入例子(#2)的參數,也就是~(~0 << 4),結果為00001111。這里將~(~0 << n)記為@@0

接著 執行 y & @@0 ,用掩碼的原理將y最右邊的n位取出。代入例子(#2)的參數及上一步的結果,應執行00000101 & 00001111,結果為00000101。這里將y & @@0記為$$1 。

最后執行 $$1 << (p+1-n),左移到對應位置上。代入例子(#2)的參數及上一步的結果,也就是00000101 << (5+1-4),結果為00010100,生成結束。

Objective-C的Runtime中的位運算應用

這里會介紹一些runtime源碼中使用位運算的例子。

判斷是否是TaggedPointer

在runtime源碼中,判斷是否是TaggedPointer的函數定義如下:

static inline bool 
_objc_isTaggedPointer(const void * _Nullable ptr)
{
    return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

其中參數const void * _Nullable ptr為對象的地址。_OBJC_TAG_MASK是一個掩碼,其宏定義比較長,我將它簡單的整理了一下:

#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__
    // 64-bit Mac - tag bit is LSB
#   define _OBJC_TAG_MASK 1UL
#else
    // Everything else - tag bit is MSB
#   define _OBJC_TAG_MASK (1UL<<63)
#endif

我們得到了結論:

64-bit Mac下, _OBJC_TAG_MASK為1UL,對應二進制為:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

其他平臺下, _OBJC_TAG_MASK 為(1UL<<63),對應二進制為:10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

根據以上結論,結合_objc_isTaggedPointer函數的代碼,很容易理解它的原理:

在64-bit MAC下,取出對象地址二進制的最低位(LSB),若最低位為1則為TaggedPointer。

在其他平臺下,取出對象地址二進制的最高位(MSB),若為1則為TaggedPointer。

isa_t

在ARM64架構之前,對象的isa指針直接指向類對象地址;在ARM64架構之后,一個8字節的isa變量額外存儲了許多與當前對象相關的信息。 我們先看來看一下最新的isa結構定義:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

相關的宏定義:

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      
      uintptr_t nonpointer        : 1;                                       
      uintptr_t has_assoc         : 1;                                       
      uintptr_t has_cxx_dtor      : 1;                                       
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ 
      uintptr_t magic             : 6;                                       
      uintptr_t weakly_referenced : 1;                                       
      uintptr_t deallocating      : 1;                                       
      uintptr_t has_sidetable_rc  : 1;                                       
      uintptr_t extra_rc          : 19
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        
      uintptr_t nonpointer        : 1;                                         
      uintptr_t has_assoc         : 1;                                         
      uintptr_t has_cxx_dtor      : 1;                                         
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ 
      uintptr_t magic             : 6;                                         
      uintptr_t weakly_referenced : 1;                                         
      uintptr_t deallocating      : 1;                                         
      uintptr_t has_sidetable_rc  : 1;                                         
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

# else
#   error unknown architecture for packed isa
# endif

上面代碼中用了c語言的聯合體以及位段的技術,當然這不是我們的重點,有興趣的話可以去了解下。 我在Mac上編寫了一段代碼,用來展示這8個字節里所存儲的數據有多么豐富。要知道,8個字節僅僅是一個long變量的大小。

#import 
#   define ISA_MASK        0x00007ffffffffff8ULL
union isa_t {
    Class cls;
    uintptr_t bits;
    struct {
        uintptr_t nonpointer        : 1;
        uintptr_t has_assoc         : 1;
        uintptr_t has_cxx_dtor      : 1;
        uintptr_t shiftcls          : 44;
        uintptr_t magic             : 6;
        uintptr_t weakly_referenced : 1;
        uintptr_t deallocating      : 1;
        uintptr_t has_sidetable_rc  : 1;
        uintptr_t extra_rc          : 8;
    };
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *obj =  [[NSObject alloc]init]; // 這塊內存記為#1,obj指向#1,#1的引用計數器+1
        NSObject *obj2 = obj; // obj2也指向#1,#1的引用計數器+1
        NSObject *obj3 = obj; // obj3也指向#1,#1的引用計數器+1
        __weak NSObject *weak_obj = obj;// 弱引用
        union isa_t _isa_t;
        void *_obj = (__bridge void *)(obj);
        _isa_t.bits = *((uintptr_t*)_obj);
        printf("是否使用isa指針優化:%x
",_isa_t.nonpointer);
        printf("是否有用關聯對象:%x
",_isa_t.has_assoc);
        printf("是否有C++析構函數:%x
",_isa_t.has_cxx_dtor);
        printf("isa取出類對象:%llx
",_isa_t.bits & ISA_MASK);
        printf("class方法取出類對象:%lx
",(long)[NSObject class]);
        printf("調試時是否完成初始化:%x
",_isa_t.magic);
        printf("是否有被弱引用指向過:%x
",_isa_t.weakly_referenced);
        printf("是否正在釋放:%x
",_isa_t.deallocating);
        printf("是否使用了sidetable:%x
",_isa_t.has_sidetable_rc);
        printf("引用計數器-1:%x
",_isa_t.extra_rc);
    }
    return 0;
}

輸出的結果:

是否使用isa指針優化:1

是否有用關聯對象:0

是否有C++析構函數:0

isa取出類對象:7fffb506f140

class方法取出類對象:7fffb506f140

調試時是否完成初始化:3b

是否有被弱引用指向過:1

是否正在釋放:0

是否使用了sidetable:0

引用計數器-1:2

方法緩存中的Hash函數

先給出Runtime源碼里從緩存中查找方法的函數:

 bucket_t * cache_t::find(cache_key_t k, id receiver)
{
    assert(k != 0);

    bucket_t *b = buckets();
    mask_t m = mask();
    mask_t begin = cache_hash(k, m);
    mask_t i = begin;
    do {
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}

再來看下cache_hash的實現:

// Class points to cache. SEL is key. Cache buckets store SEL+IMP.
// Caches are never built in the dyld shared cache.
static inline mask_t cache_hash(cache_key_t key, mask_t mask) 
{
    return (mask_t)(key & mask);
}

這里需要說明cache_hash函數中幾個參數的意義:

key: 方法SEL的地址(8字節64位)

mask: 哈希表長度 -1

(key & mask)的結果能保證在[0,mask]整數范圍內,所以可以正確的映射到Hash表上。

NS_OPTIONS

NS_OPTIONS如其名「選項」,可以讓你在一個8字節NSUInteger變量中最多保存64個選項開關。 先來看看KVO中NSKeyValueObservingOptions的定義:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

    NSKeyValueObservingOptionNew = 0x01,
    NSKeyValueObservingOptionOld = 0x02,
    NSKeyValueObservingOptionInitial = 0x04,
    NSKeyValueObservingOptionPrior = 0x08
};

一共有4個選項,其對應的二進制分別為:

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000001

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000010

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000100

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000001000

可以看得出,每個選項都是獨立一個1,并且和其他選項的位置不一樣。如果對某幾個選項進行或運算(|)就會合并它們的選項。 舉個平時常用的例子:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 的結果為:

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 000000011

下面給出讀取這些選項的代碼:

- (void)readOptions:(NSKeyValueObservingOptions)options {
    NSLog(@"---------------begin---------------");
    if (options & NSKeyValueObservingOptionNew ) {
        NSLog(@"contain NSKeyValueObservingOptionNew");
    }
    if (options & NSKeyValueObservingOptionOld ) {
        NSLog(@"contain NSKeyValueObservingOptionOld");
    }
    if (options & NSKeyValueObservingOptionInitial ) {
         NSLog(@"contain NSKeyValueObservingOptionInitial");
    }
    if (options & NSKeyValueObservingOptionPrior ) {
         NSLog(@"contain NSKeyValueObservingOptionPrior");
    }
     NSLog(@"---------------end-----------------");
}

// 輸出
/*
     ---------------begin---------------
     contain NSKeyValueObservingOptionNew
     contain NSKeyValueObservingOptionOld
     ---------------end-----------------
     ---------------begin---------------
     contain NSKeyValueObservingOptionNew
     contain NSKeyValueObservingOptionInitial
     contain NSKeyValueObservingOptionPrior
     ---------------end-----------------
*/

調用:

 [self readOptions:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew];
 [self readOptions:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial |NSKeyValueObservingOptionPrior];

輸出:

/*
     ---------------begin---------------
     contain NSKeyValueObservingOptionNew
     contain NSKeyValueObservingOptionOld
     ---------------end-----------------
     ---------------begin---------------
     contain NSKeyValueObservingOptionNew
     contain NSKeyValueObservingOptionInitial
     contain NSKeyValueObservingOptionPrior
     ---------------end-----------------
*/

參考資料

《C語言程序設計(K & R)》


本篇已同步到個人博客:位運算世界暢游指南

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/7071.html

相關文章

  • IT巨頭互掐云計算:安全性和耗能存爭議

    摘要:問題安全性和耗能存爭議云計算已經成為全球未來信息產業發展的戰略方向,隨著各國都認真研究云計算將為社會和經濟發展模式帶來的變革,部署國家戰略,中國也正在推動云從概念走向應用。   身邊就是一片云  ■ 體驗  白領蕭瀟(化名)對任何科技新知都難以表現出興趣,云?聽說過,是什么?很快她就投入到了對附近商場打折活動的熱切關注中。其實,像蕭瀟一樣,我們往往享受到了云的便利,卻又對它視而不見。每一天,...

    Miracle_lihb 評論0 收藏0
  • IT巨頭互掐云計算:安全性和耗能存爭議

    摘要:問題安全性和耗能存爭議云計算已經成為全球未來信息產業發展的戰略方向,隨著各國都認真研究云計算將為社會和經濟發展模式帶來的變革,部署國家戰略,中國也正在推動云從概念走向應用。   身邊就是一片云 ?   體驗?  白領蕭瀟(化名)對任何科技新知都難以表現出興趣,云?聽說過,是什么?很快她就投入到了對附近商場打折活動的熱切關注中。其實,像蕭瀟一樣,我們往往享受到了云的便利,卻又對它視而不見。每一...

    libxd 評論0 收藏0
  • 云計算將被大范圍濫用 耗能存爭議

    摘要:預計年全國云計算產業鏈規模可能達到億至一萬億元人民幣,有望占到年戰略性新興產業以上的產值規模。但也有分析人士認為,云計算可以節省大量企業的硬件成本和軟件成本,而云計算服務器所耗費的能源應該不會多于大量企業節省的能源。 云在哪里?其實,云計算技術在網絡服務中已經隨處可見,例如云桌面、云辦公、云視頻、云游戲、云搜索、云識別、云U盤等。用戶不必了解其背后的運行原理,也無需自備一些硬件和軟件,就可以...

    Lucky_Boy 評論0 收藏0
  • 【算法技巧】運算裝逼指南

    摘要:位算法的效率有多快我就不說,不信你可以去用億個數據模擬一下,今天給大家講一講位運算的一些經典例子。不過,最重要的不是看懂了這些例子就好,而是要在以后多去運用位運算這些技巧,當然,采用位運算,也是可以裝逼的,不信,你往下看。位算法的效率有多快我就不說,不信你可以去用 10 億個數據模擬一下,今天給大家講一講位運算的一些經典例子。不過,最重要的不是看懂了這些例子就好,而是要在以后多去運用位運算這...

    _ang 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<