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

資訊專欄INFORMATION COLUMN

位運(yùn)算世界暢游指南

番茄西紅柿 / 1297人閱讀

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

目錄

概念介紹

基本位運(yùn)算符

按位與 ( AND )

按位或 ( OR )

按位異或 ( XOR )

取反 ( NOT )

右移 ( >> )

左移 ( << )

基礎(chǔ)技巧

將某些二進(jìn)制位設(shè)置為1

掩碼

取出第i位二進(jìn)制值

計(jì)算無符號(hào)變量的取值范圍

奇偶判斷

統(tǒng)計(jì)二進(jìn)制中1個(gè)數(shù)

交換兩個(gè)變量的值(無臨時(shí)變量)

子集生成

進(jìn)階技巧

getBits

setBits

Objective-C的Runtime中的位運(yùn)算應(yīng)用

判斷是否是TaggedPointer

isa_t

方法緩存中的Hash函數(shù)

NS_OPTIONS

參考資料

概念介紹

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

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

    我們?cè)谟懻摱M(jìn)制的時(shí)候,位序一般都是從右(低位)到左(高位)的。舉個(gè)例子,對(duì)于二進(jìn)制0011來說,第0位是1,第1位是1,第二位是0,第三位是0。

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

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

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

基本位運(yùn)算符

位運(yùn)算符就像是控制比特位的扳手,在學(xué)習(xí)位運(yùn)算前先介紹下每個(gè)運(yùn)算符的意義及其用法。

按位與 ( AND )

運(yùn)算規(guī)則:只有在兩個(gè)值都為1時(shí)結(jié)果才為1,兩個(gè)值中有一個(gè)為0結(jié)果便為0。在編程語言里一般用 & 表示與運(yùn)算符。


舉個(gè)例子,10100001 & 10001001 = 10000001。(注:操作數(shù)都為二進(jìn)制。)

按位或 ( OR )

運(yùn)算規(guī)則: 兩個(gè)值中有一個(gè)為1結(jié)果便為1,兩個(gè)值都為0時(shí)結(jié)果才為0。在編程語言里一般用 | 表示或運(yùn)算符。


舉個(gè)例子,10100001 | 10001001 = 10101001。


按位異或 ( XOR )

運(yùn)算規(guī)則: 只有當(dāng)兩個(gè)值不相同時(shí)結(jié)果才為1,兩個(gè)值相同時(shí)結(jié)果為0。在編程語言里一般用 ^ 表示異或運(yùn)算符。


舉個(gè)例子,10100001 ^ 10001001 = 00101000。

取反 ( NOT )

在數(shù)值的二進(jìn)制表示方式上,將0變?yōu)?,將1變?yōu)?。在編程語言里一般用 ~ 表示取反運(yùn)算符。
來看一個(gè)例子可能會(huì)更加直觀:

右移 ( >> )

右移將操作數(shù)的二進(jìn)制位整體向右移動(dòng)N位,空出部分用0填充,在編程語言里一般用 >>表示右移運(yùn)算符。

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


左移 ( << )

左移將操作數(shù)的二進(jìn)制位整體向左移動(dòng)N位,空出部分用0填充,在編程語言里一般用 << 表示左移運(yùn)算符。

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

基礎(chǔ)技巧

這里先介紹一些比較簡(jiǎn)單實(shí)用的位運(yùn)算技巧,這些技巧在日常開發(fā)中也是比較常用的。

將某些二進(jìn)制位設(shè)置為1

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

x = x | SET_ON ,該語句將x中對(duì)應(yīng)于SET_ON中為1的二進(jìn)制位設(shè)置為1;x中對(duì)應(yīng)于SET_ON中為0的二進(jìn)制位保持不變。

掩碼

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


取出第i位二進(jìn)制值

這個(gè)函數(shù)傳入一個(gè)data,返回其二進(jìn)制從右邊開始數(shù)第i位的值。

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

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

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

計(jì)算無符號(hào)變量的取值范圍

筆者在mac上的unsigned long 是8個(gè)字節(jié),可以存儲(chǔ)64位二進(jìn)制,由于沒有符號(hào)位,故只需將這64位二進(jìn)制都填充為1就得到unsigned long變量的最大值了。

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

奇偶判斷

如果最后一位二進(jìn)制為0,那么這個(gè)數(shù)字就是偶數(shù),如果為1就是奇數(shù)。這里給出實(shí)現(xiàn)函數(shù):

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

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

關(guān)于負(fù)數(shù):雖然負(fù)數(shù)在計(jì)算機(jī)中以補(bǔ)碼的方式存儲(chǔ),但由于補(bǔ)碼最后一位和原碼最后一位相同,所以上面的函數(shù)同樣適用于負(fù)數(shù)。為什么呢?舉個(gè)例子:

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

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

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

統(tǒng)計(jì)二進(jìn)制中1個(gè)數(shù)
int x =0xba;//10111010
int count = 0;
while (x!=0) {
    x = x&(x-1);
    count++;
}
printf("%d
",count);

循環(huán)中每次執(zhí)行x = x&(x-1)后,x的二進(jìn)制的最后一個(gè)1就會(huì)被消去。當(dāng)所有1都被消去后,count計(jì)數(shù)完畢,x=0,退出循環(huán)。

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

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

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

可以發(fā)現(xiàn)規(guī)律:

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

當(dāng)x=nn..nn1這種形式時(shí),x-1=nn..nn0。 這個(gè)時(shí)候x & (x-1) = nn..nn1 & nn..nn0 = nn..nn0,x最后一個(gè)1被消去。

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

想要了解原理,需要先知道幾個(gè)異或運(yùn)算的性質(zhì):

交換律:a^b = b^a

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

恒等律:a^0 = a

規(guī)零律:a^a = 0

自反性:a^b^b = a

假設(shè)一開始,a=k,b=t

執(zhí)行a=a^ba=k^t;

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

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

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

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

 char *a = "a-a-a-a-a-a";// 存儲(chǔ)在數(shù)據(jù)區(qū)的字符串常量
 char *b = "b-b-b-b-b-b";//存儲(chǔ)在數(shù)據(jù)區(qū)的字符串常量
 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
 */

子集生成

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

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

編號(hào) A的子集 人類思考過程 二進(jìn)制表示
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

細(xì)心的話,應(yīng)該能發(fā)現(xiàn)上面的表格中的編號(hào)和二進(jìn)制剛剛好能對(duì)的上。所以對(duì)于有個(gè)n個(gè)元素的集合,只要生成0到2^n-1個(gè)整數(shù)編號(hào),然后根據(jù)每個(gè)編號(hào)對(duì)應(yīng)的二進(jìn)制解析出相應(yīng)的子集即可。 下面是c語言實(shí)現(xiàn)的代碼:

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

進(jìn)階技巧

這里介紹C語言程序設(shè)計(jì)這本書中的兩個(gè)非常實(shí)用的函數(shù),相信你在平時(shí)的項(xiàng)目中也能應(yīng)用的到。

getBits

該函數(shù)用來返回x中從右邊數(shù)第p位開始向右數(shù)n位二進(jìn)制。

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

舉個(gè)例子,調(diào)用getBits(168,5,3)后,返回168對(duì)應(yīng)二進(jìn)制10101000從右邊數(shù)第5位開始向右3位二進(jìn)制,也就是101。可以結(jié)合下圖理解:

下面來說以下原理:

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


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

    一開始執(zhí)行~0生成全1的二進(jìn)制,11111111。

    然后左移3位變成11111000。

    最后執(zhí)行圓括號(hào)左邊的~,取反變成00000111,現(xiàn)在掩碼生成完成。


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

setBits

該函數(shù)返回x中從第p位開始的n個(gè)(二進(jìn)制)位設(shè)置為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);
}

舉個(gè)例子(#2),調(diào)用setbits(168, 5, 4, 0b0101)后,將168對(duì)應(yīng)二進(jìn)制10101000從右邊數(shù)第5位開始的4個(gè)二進(jìn)制位設(shè)置為0101,設(shè)置完后變成10010100,最后將結(jié)果返回。可以結(jié)合下圖理解:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Objective-C的Runtime中的位運(yùn)算應(yīng)用

這里會(huì)介紹一些runtime源碼中使用位運(yùn)算的例子。

判斷是否是TaggedPointer

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

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

其中參數(shù)const void * _Nullable ptr為對(duì)象的地址。_OBJC_TAG_MASK是一個(gè)掩碼,其宏定義比較長(zhǎng),我將它簡(jiǎn)單的整理了一下:

#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

我們得到了結(jié)論:

64-bit Mac下, _OBJC_TAG_MASK為1UL,對(duì)應(yīng)二進(jìn)制為:00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

其他平臺(tái)下, _OBJC_TAG_MASK 為(1UL<<63),對(duì)應(yīng)二進(jìn)制為:10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

根據(jù)以上結(jié)論,結(jié)合_objc_isTaggedPointer函數(shù)的代碼,很容易理解它的原理:

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

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

isa_t

在ARM64架構(gòu)之前,對(duì)象的isa指針直接指向類對(duì)象地址;在ARM64架構(gòu)之后,一個(gè)8字節(jié)的isa變量額外存儲(chǔ)了許多與當(dāng)前對(duì)象相關(guān)的信息。 我們先看來看一下最新的isa結(jié)構(gòu)定義:

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
};

相關(guān)的宏定義:

# 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語言的聯(lián)合體以及位段的技術(shù),當(dāng)然這不是我們的重點(diǎn),有興趣的話可以去了解下。 我在Mac上編寫了一段代碼,用來展示這8個(gè)字節(jié)里所存儲(chǔ)的數(shù)據(jù)有多么豐富。要知道,8個(gè)字節(jié)僅僅是一個(gè)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]; // 這塊內(nèi)存記為#1,obj指向#1,#1的引用計(jì)數(shù)器+1
        NSObject *obj2 = obj; // obj2也指向#1,#1的引用計(jì)數(shù)器+1
        NSObject *obj3 = obj; // obj3也指向#1,#1的引用計(jì)數(shù)器+1
        __weak NSObject *weak_obj = obj;// 弱引用
        union isa_t _isa_t;
        void *_obj = (__bridge void *)(obj);
        _isa_t.bits = *((uintptr_t*)_obj);
        printf("是否使用isa指針優(yōu)化:%x
",_isa_t.nonpointer);
        printf("是否有用關(guān)聯(lián)對(duì)象:%x
",_isa_t.has_assoc);
        printf("是否有C++析構(gòu)函數(shù):%x
",_isa_t.has_cxx_dtor);
        printf("isa取出類對(duì)象:%llx
",_isa_t.bits & ISA_MASK);
        printf("class方法取出類對(duì)象:%lx
",(long)[NSObject class]);
        printf("調(diào)試時(shí)是否完成初始化:%x
",_isa_t.magic);
        printf("是否有被弱引用指向過:%x
",_isa_t.weakly_referenced);
        printf("是否正在釋放:%x
",_isa_t.deallocating);
        printf("是否使用了sidetable:%x
",_isa_t.has_sidetable_rc);
        printf("引用計(jì)數(shù)器-1:%x
",_isa_t.extra_rc);
    }
    return 0;
}

輸出的結(jié)果:

是否使用isa指針優(yōu)化:1

是否有用關(guān)聯(lián)對(duì)象:0

是否有C++析構(gòu)函數(shù):0

isa取出類對(duì)象:7fffb506f140

class方法取出類對(duì)象:7fffb506f140

調(diào)試時(shí)是否完成初始化:3b

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

是否正在釋放:0

是否使用了sidetable:0

引用計(jì)數(shù)器-1:2

方法緩存中的Hash函數(shù)

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

 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的實(shí)現(xiàn):

// 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函數(shù)中幾個(gè)參數(shù)的意義:

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

mask: 哈希表長(zhǎng)度 -1

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

NS_OPTIONS

NS_OPTIONS如其名「選項(xiàng)」,可以讓你在一個(gè)8字節(jié)NSUInteger變量中最多保存64個(gè)選項(xiàng)開關(guān)。 先來看看KVO中NSKeyValueObservingOptions的定義:

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

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

一共有4個(gè)選項(xiàng),其對(duì)應(yīng)的二進(jìn)制分別為:

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

可以看得出,每個(gè)選項(xiàng)都是獨(dú)立一個(gè)1,并且和其他選項(xiàng)的位置不一樣。如果對(duì)某幾個(gè)選項(xiàng)進(jìn)行或運(yùn)算(|)就會(huì)合并它們的選項(xiàng)。 舉個(gè)平時(shí)常用的例子:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld 的結(jié)果為:

0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 0000000000 000000011

下面給出讀取這些選項(xiàng)的代碼:

- (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-----------------
*/

調(diào)用:

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

輸出:

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

參考資料

《C語言程序設(shè)計(jì)(K & R)》


本篇已同步到個(gè)人博客:位運(yùn)算世界暢游指南

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/7071.html

相關(guān)文章

  • IT巨頭互掐云計(jì)算:安全性和耗能存爭(zhēng)議

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

    Miracle_lihb 評(píng)論0 收藏0
  • IT巨頭互掐云計(jì)算:安全性和耗能存爭(zhēng)議

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

    libxd 評(píng)論0 收藏0
  • 云計(jì)算將被大范圍濫用 耗能存爭(zhēng)議

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

    Lucky_Boy 評(píng)論0 收藏0
  • 【算法技巧】運(yùn)算裝逼指南

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

    _ang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<