二級指針指向的數據結構是什么樣的?

//代碼片段摘自蘋果開源的runtime代碼(objc4-208版本)
//https://opensource.apple.com/source/objc4/objc4-208/runtime/objc-class.h
struct objc_class {         
    struct objc_class *isa; 
    struct objc_class *super_class; 
    const char *name;       
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;

#if defined(Release3CompatibilityBuild)
    struct objc_method_list *methods;
#else
    struct objc_method_list **methodLists;
#endif

    struct objc_cache *cache;
    struct objc_protocol_list *protocols;
};

這是Objective-C 2.0中的類的代碼,相信做iOS開發的同學都很熟悉的了。有天在查資料又看到它的時候,想到了一個好奇的問題:

methodLists 是一個二級指針,在內存中,它指向的是什么呢?(或者說,其指向的數據結構到底是怎么樣的?)

然后,我想到了下面幾個可能性:

  • methodLists 指向的是一個結構體的指針

    aP-bP-obj.png
  • methodLists 指向的是結構體數組的指針

aP-bP-objArray.png
  • methodLists 指向的是結構體指針數組
aP-bPArray.png

那么上面的可能性都是存在的嗎?為了驗證,我寫了下面的代碼進行測試。

#include <stdio.h>

typedef struct {
  float version;
} method;


int main(int argc, char const *argv[]) {
  method **methodList;

  method aMethod;
  aMethod.version = 1.1;

  method bMethod;
  bMethod.version = 2.2;

  printf("開始驗證第1種可能性:aP->bP->obj\n");
  method *aMethodP = &aMethod;
  methodList = &aMethodP;

  method* currentMethod = *methodList;
  float version = currentMethod->version;
  printf("version:%0.1f\n",version);

  printf("結束驗證第1種可能性。\n");

  printf("開始驗證第2種可能性:aP->bP->obj[]\n");
  method methodArray[2];
  methodArray[0] = aMethod;
  methodArray[1] = bMethod;

  *methodList = methodArray;
  for (size_t i = 0; i < 2; i++) {
    method* currentMethod = &((*methodList)[i]);
    float version = currentMethod->version;
    printf("version:%0.1f\n",version);
  }
  printf("結束驗證第2種可能性。\n");

  printf("開始驗證第3種可能性:aP->[(bP->obj)]\n");
  method* methodPointArray[2];
  methodPointArray[0]=&aMethod;
  methodPointArray[1]=&bMethod;

  methodList = &(methodPointArray[0]);
  for (size_t i = 0; i < 2; i++) {
    method* currentMethod = *(methodList+i);
    float version = currentMethod->version;
    printf("version:%0.1f\n",version);
  }
  printf("結束驗證第3種可能性。\n");

  return 0;
}

運行結果如下:

二級指針可能性運行結果.png

按照上面提出的模型所編寫的代碼可以運行通過,這證明所說的3種可能性都存在,然而在runtime里使用的是哪種呢?

為此,特意去看了相關的代碼,得到的答案是:第三種。

論據為:runtime中的class移除method的方法代碼:

//代碼片段摘自蘋果開源的runtime代碼(objc4-208版本)
//https://opensource.apple.com/source/objc4/objc4-208/runtime/objc-class.m

void class_removeMethods (Class cls, struct objc_method_list * meths)
{
    // Remove atomically.
    _objc_removeMethods (meths, &((struct objc_class *) cls)->methodLists);
    
    // Must flush when dynamically removing methods.  No need to flush
    // all the class method caches.  If cls is a meta class, though,
    // this will still flush it and any of its sub-meta classes.
    flush_caches (cls, NO); 
}
//代碼片段摘自蘋果開源的runtime代碼(objc4-208版本)
//https://opensource.apple.com/source/objc4/objc4-208/runtime/objc-runtime.m

void _objc_removeMethods (struct objc_method_list * mlist, struct objc_method_list *** list)
{
    struct objc_method_list **  ptr;
 
        // Locate list in the array 
        ptr = *list;
        while (*ptr != mlist) {
                // fix for radar # 2538790
                if ( *ptr == END_OF_METHODS_LIST ) return;
                ptr += 1;
        }
 
        // Remove this entry 
        *ptr = 0;
  
        // Left shift the following entries
        while (*(++ptr) != END_OF_METHODS_LIST)
                *(ptr-1) = *ptr;
        *(ptr-1) = 0;
}

寫在最后

那么對于標題提出的問題:『二級指針指向的數據結構是什么樣的?』,它的標準答案是什么呢?

答案是:沒有標準答案,它的答案應該是結合具體的業務代碼來回答的,比如Runtime里的methodLists。

另外,在最后,不得不感嘆下:指針真是C語言的靈魂,其讓C變得何其靈活!(當然,同時也讓C變得復雜,在你不知道作者的指針意圖的時候)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容