<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Objective-C之Class底層結構探索

      isa 走位圖

      在講 OC->Class 底層類結構之前,先看下下面這張圖:

      isa走位

      通過isa走位圖 得出的結論是:
      1,類,父類,元類都包含了 isa, superclass

      2,對象isa指向類對象,類對象的isa指向了元類,元類的 isa 指向了根元類,根元類 isa 指向自己

      3,類的 superclass 指向父類,父類的 superclass 指向的根類,根類的superclass 指向的nil

      4,元類的 superclass 指向父元類,父元類 superclass 指向的根元類,根元類 superclass 指向根類,根類 superclass 指向nil

      這下又復習了 isasuperclass 走位;那么問題這些類,類對象,元類對象當中的在底層展現的數據結構是怎樣呢,這是我需要探索的,于是把源碼貼出來展開分析下:

      struct objc_class

      struct objc_class : objc_object {
          // Class ISA;
          Class superclass; 
          cache_t cache;             // formerly cache pointer and vtable
          class_data_bits_t bits;  
          class_rw_t *data() const {
              return bits.data();
          }
          const class_ro_t *safe_ro() const {
              return bits.safe_ro();
          }
      }
      

      從源碼沒見 isa 屬性,其實它繼承了objc_object ,而 objc_object 中有個isa ,在運行時類圖生成中會產生一個isa 指向objc_object 這個類圖,而 superclass 指向它的父類;根據上面 isa , superclass 走位圖就知道它的指向關系。

      cache_t & class_data_bits_t

      cache 方法緩存,這個作用將常調用的方法緩存下來;便于下次直接查找調用,提高查找效率。
      它的結構:

      struct cache_t {
      	struct bucket_t *buckets() const;//存儲方法的散列表
      	mask_t mask() const;//散列表緩存長度
      	mask_t occupied() const;//已緩存方法個數
      }
      
      struct class_data_bits_t {
          class_rw_t* data() const;//類信息
      }
      
      

      bits 存儲具體類信息,它需要&FAST_DATA_MASK來計算得到類心所有信息,源碼如下:

      FAST_DATA_MASK 掩碼值

      imageng

      bool has_rw_pointer() const {
      	#if FAST_IS_RW_POINTER
      	        return (bool)(bits & FAST_IS_RW_POINTER);
      	#else
      	        class_rw_t *maybe_rw = (class_rw_t *)(bits & FAST_DATA_MASK);
      	        return maybe_rw && (bool)(maybe_rw->flags & RW_REALIZED);
      	#endif
      }
      

      通過源碼確實需要這種方式計算能得到類的存儲信息;那為什么要用這種方式去處理呢。
      比如說我要得到存儲在 class_rw_t 類信息信息我只要通過 FAST_DATA_MASK 掩碼值就能得到它的地址信息,通過地址信息就能從內存中拿到所有類的存儲信息。

      那這樣我的FAST_DATA_MASK掩碼值不一樣,我通過&計算,得到的數據信息也就不一樣,不得不說蘋果工程師想的周到,而且這種方式不僅isa也是這樣,很多地方都用這種方式取值,大大提高訪問速度,數據提取效率。

      class_rw_t ,class_ro_t,class_rw_ext_t

      struct class_rw_t {
           const class_ro_t *ro() const ;
           const method_array_t methods() const ;//如果是類對象:放對象方法,元類:元類對象方法
           
           const property_array_t properties() const;
           const protocol_array_t protocols() const;
           class_rw_ext_t *ext() const;
      }
      struct class_rw_ext_t {
          method_array_t methods;
          property_array_t properties;
          protocol_array_t protocols;
          uint32_t version;
      }
      

      可以看出類的信息具體就存儲在class_rw_tclass_ro_tclass_rw_ext_t 中,

      剖析下class_rw_t
      先看看method_array_tproperty_array_tprotocol_array_t源碼結構

      class property_array_t : 
          public list_array_tt<property_t, property_list_t, RawPtr>
      {
          typedef list_array_tt<property_t, property_list_t, RawPtr> Super;
      
       public:
          property_array_t() : Super() { }
          property_array_t(property_list_t *l) : Super(l) { }
      };
      
      
      class protocol_array_t : 
          public list_array_tt<protocol_ref_t, protocol_list_t, RawPtr>
      {
          typedef list_array_tt<protocol_ref_t, protocol_list_t, RawPtr> Super;
      
       public:
          protocol_array_t() : Super() { }
          protocol_array_t(protocol_list_t *l) : Super(l) { }
      };
      

      看完之后,他們都繼承list_array_tt,那么 list_array_tt 是什么鬼,它數據結構是怎樣的,這下在取找下它。源碼如下:

      template <typename Element, typename List, template<typename> class Ptr>
      class list_array_tt {
       protected:
          template <bool authenticated>
          class iteratorImpl {
              const Ptr<List> *lists;
              const Ptr<List> *listsEnd;
          }
              
          using iterator = iteratorImpl<false>;
          using signedIterator = iteratorImpl<true>;
      
       public:
          list_array_tt() : list(nullptr) { }
          list_array_tt(List *l) : list(l) { }
          list_array_tt(const list_array_tt &other) {
              *this = other;
          }
      
          void attachLists(List* const * addedLists, uint32_t addedCount) {
              if (addedCount == 0) return;
      
              if (hasArray()) {
                  // many lists -> many lists
                  uint32_t oldCount = array()->count;
                  uint32_t newCount = oldCount + addedCount;
                  array_t *newArray =(array_t*)malloc(array_t::byteSize(newCount));
                  newArray->count = newCount;
                  array()->count = newCount;
      
                  for (int i = oldCount - 1; i >= 0; i--)
                      newArray->lists[i + addedCount] = array()->lists[i];
                  for (unsigned i = 0; i < addedCount; i++)
                      newArray->lists[i] = addedLists[i];
                  free(array());
                  setArray(newArray);
                  validate();
              }
              else if (!list  &&  addedCount == 1) {
                  // 0 lists -> 1 list
                  list = addedLists[0];
                  validate();
              } 
              else {
                  // 1 list -> many lists
                  Ptr<List> oldList = list;
                  uint32_t oldCount = oldList ? 1 : 0;
                  uint32_t newCount = oldCount + addedCount;
                  setArray((array_t *)malloc(array_t::byteSize(newCount)));
                  array()->count = newCount;
                  if (oldList) array()->lists[addedCount] = oldList;
                  for (unsigned i = 0; i < addedCount; i++)
                      array()->lists[i] = addedLists[i];
                  validate();
              }
          }
          
      }
      

      我把主要地方拿去出來,可以看到 attachLists 它的目的是將一個或多個列表(List 類型)附加到某個 list_array_tt對象中。這個對象可以包含零個、一個或多個列表,這些列表可以是單個指針,也可以是指針數組。函數的輸入參數是一個指向 List 指針數組的指針 addedLists 和一個無符號整數 addedCount,表示要添加的列表數量。

      由此我推斷它是一個數組,而且是一個二維數組存儲的,所有由此得出 class_rw_tmethodspropertiesprotocols這幾個屬性利用二維數組取存儲類的方法,協議等信息,而且是可讀可寫的屬性。

      那它設計這種二維數組有什么好處呢?當然有好處,它可以動態的給數組里面增加刪除方法,很方便我們分類方法的編寫完進行存儲。

      那搞清楚了 class_rw_t 幾個重要數據存儲信息,那 class_rw_t 它的作用是干什么的呢;

      class_rw_t 結構體定義來看;它是在應用運行時,將OC類,分類的信息直接寫入到class_rw_t結構的數據結構中,在類的方法,協議進行調用時,從里面去讀取,然后常調用的方法,又存儲在cache_t這個結構體中,可想而知,蘋果對OC類的處理,煞費苦心。

      struct class_ro_t

      class_rw_t結構體中有個 class_ro_t 結構體,在探索下這個東西做什么的,它的源碼如下:

      struct class_ro_t {
          WrappedPtr<method_list_t, method_list_t::Ptrauth> baseMethods;
          protocol_list_t * baseProtocols;
          const ivar_list_t * ivars;
          property_list_t *baseProperties;
      }
      

      先說說 ivars 這個屬性修飾的結構體源碼如下:

      struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
          bool containsIvar(Ivar ivar) const {
              return (ivar >= (Ivar)&*begin()  &&  ivar < (Ivar)&*end());
          }
      };
      

      這個貌似只有一個繼承 entsize_list_tt,那在探索下源碼:

      struct entsize_list_tt {
          uint32_t entsizeAndFlags;
          uint32_t count;
           struct iteratorImpl {
           uint32_t entsize;
              uint32_t index;  // keeping track of this saves a divide in operator-
      
              using ElementPtr = std::conditional_t<authenticated, Element * __ptrauth(ptrauth_key_process_dependent_data, 1, 0xdead), Element *>;
      
              ElementPtr element;
      
              typedef std::random_access_iterator_tag iterator_category;
              typedef Element value_type;
              typedef ptrdiff_t difference_type;
              typedef Element* pointer;
              typedef Element& reference;
      
              iteratorImpl() { }
      
              iteratorImpl(const List& list, uint32_t start = 0)
                  : entsize(list.entsize())
                  , index(start)
                  , element(&list.getOrEnd(start))
              { }
           }
      }
      

      可以看出這段代碼定義了一個結構體 entsize_list_tt,它內部包含一個嵌套的結構體 iteratorImpl,用于實現一個迭代器。遍歷容器(如列表、數組等)的對象。

      到此可以得出ivars 是一個 ivar_list_t 數組,它存儲了類的屬性變量信息,那protocol_list_t結構體內部也是數組形式構建的。

      baseProtocolsbaseProperties 這兩個屬性對類的存儲信息只能讀取,不能寫入。

      所以總結的是:從 class_ro_t 結構體定義來看,它存儲類的變量,方法,協議信息,而且這個結構體屬于類的只讀信息,它包含了類的初始信息。

      class_rw_ext_t

      這個結構體不在過多敘述,簡單來說它是基于 class_rw_t 之后為了更好管理oc類的高級特性,比如關聯屬性等,衍生出來的一個結構體,包括:method_array_t ,property_arrat_t ,protocol_array_t 等定義屬性類型

      到這里類結構及存儲所關聯的信息都在這里了;來一張他們關聯的結構思維圖:

      imageng

      總結:一開始編譯時,程序將類的初始信息放在 class_ro_t中,當程序運行時,將類的信息合并在一起的時候,它會將 class_ro_t 類的信息合并到 class_rw_t 結構體中去。

      struct method_t

      為什么要說method_t,因為它不僅在 class_ro_t 有使用,在OC底層其他地方也有使用;比如如下源碼:

      void method_exchangeImplementations(Method m1Signed, Method m2Signed)
      {
          if (!m1Signed  ||  !m2Signed) return;
      
          method_t *m1 = _method_auth(m1Signed);
          method_t *m2 = _method_auth(m2Signed);
      
          mutex_locker_t lock(runtimeLock);
      
          IMP imp1 = m1->imp(false);
          IMP imp2 = m2->imp(false);
          SEL sel1 = m1->name();
          SEL sel2 = m2->name();
      
          m1->setImp(imp2);
          m2->setImp(imp1);
      
      
          // RR/AWZ updates are slow because class is unknown
          // Cache updates are slow because class is unknown
          // fixme build list of classes whose Methods are known externally?
      
          flushCaches(nil, __func__, [sel1, sel2, imp1, imp2](Class c){
              return c->cache.shouldFlush(sel1, imp1) || c->cache.shouldFlush(sel2, imp2);
          });
      
          adjustCustomFlagsForMethodChange(nil, m1);
          adjustCustomFlagsForMethodChange(nil, m2);
      }
      
      static IMP
      _method_setImplementation(Class cls, method_t *m, IMP imp)
      {
          lockdebug::assert_locked(&runtimeLock);
      
          if (!m) return nil;
          if (!imp) return nil;
      
          IMP old = m->imp(false);
          SEL sel = m->name();
      
          m->setImp(imp);
      
          // Cache updates are slow if cls is nil (i.e. unknown)
          // RR/AWZ updates are slow if cls is nil (i.e. unknown)
          // fixme build list of classes whose Methods are known externally?
      
          flushCaches(cls, __func__, [sel, old](Class c){
              return c->cache.shouldFlush(sel, old);
          });
      
          adjustCustomFlagsForMethodChange(cls, m);
      
          return old;
      }
      
      
      

      方法交換,實現中底層都有用到,我們探索下,先看看 method_t 源碼:

      struct method_t {
      
          // The representation of a "big" method. This is the traditional
          // representation of three pointers storing the selector, types
          // and implementation.
          struct big {
              SEL name;
              const char *types;
              MethodListIMP imp;
          };
      
          // A "big" method, but name is signed. Used for method lists created at runtime.
          struct bigSigned {
              SEL __ptrauth_objc_sel name;
              const char * ptrauth_method_list_types types;
              MethodListIMP imp;
          };
      
          // ***HACK: This is a TEMPORARY HACK FOR EXCLAVEKIT. It MUST go away.
          // rdar://96885136 (Disallow insecure un-signed big method lists for ExclaveKit)
      #if TARGET_OS_EXCLAVEKIT
          struct bigStripped {
              SEL name;
              const char *types;
              MethodListIMP imp;
          };
      #endif
      
      }
      

      可以看到這結構體中掐套了多個結構體;在把它簡化下:

      struct method_t {
          SEL name;//方法名
          const char *types;//包含函數具有參數編碼的字符串類型的返回值
          MethodListIMP imp;//函數指針(指向函數地址的指針)
      }
      

      SEL :函數名,沒特別的意義;

      特點:
      1,使用@selector()sel_registerName()獲得
      2,使用sel_getName()NSStringFromSelector()轉成字符串
      3,不同類中相同名字方法,對應的方法選擇器是相同或相等的

      底層代碼結構:

      /// An opaque type that represents a method selector.
      typedef struct objc_selector *SEL;
      

      types:包含了函數返回值、參數編碼的字符串

      imageng
      imageng

      可以看到types在值:v16@0:8 ,可以看出name,types,IMP其實都在class_ro_t結構體中,這樣確實證明了之前說的;class_ro_t結構體在運行時存儲著類的初始狀態數據。

      v16@0:8說明下:

      v:方法返回類型,這里說void,

      16:第一個參數,

      @:id類型第二個參數,

      0:第三個參數

      : :selector類型

      8:第四個參數

      那這種types參數又是什么鬼東西,查下了資料這叫:Type Encoding(類型編碼)
      怎么證明了,使用如下代碼:
      imagepng

      蘋果官網types encoding表格:
      imageng

      IMP 其實就是指向函數的指針,感覺這個就沒有必要講了。

      struct cache_t

      cache_t 用于 class的方法緩存,對class常調用的方法緩存下來,提高查詢效率,這個上之前都已經說過;接下來看看 bucket_t

      struct bucket_t

      struct bucket_t {
      	cache_key_t _key;//函數名
      	IMP _imp;//函數內存地址
      }
      

      這種散列表的模型,其實在底層用一個數組展現:

      imagng

      其實它的內部就是一個一維數組,那可能問了,數組難道它是循環查找嗎,其實不然;在它元素超找時,它是拿到你的 函數名 & mask,而這個 mask 就是 cache_t 結構體中的 mask值;計算得到函數在 散列表 存儲的索引值,在通過索引拿到函數地址,進行執行。

      接下來看個事例:

      int main(int argc, const char * argv[]) {
      
          @autoreleasepool {
      
              Student *stu=[Student new];
      
              [stu test];
      
              [stu test];
      
              [stu test];
      
              [stu test];
      
          }
      
          return 0;
      
      }
      

      如上方法:當首次調用它會去類對象中查找,在方法執行時,他會放入cache_t 緩存中,當第二次,第三次,第四次時,它就去緩存中查找。

      imagpng

      當方法執行后;我們看到 _mask 是:3,這個3代表了我類中定義了三個函數;而——_occupied 是一個隨意的值;它其實代表了換存方法的個數。

      那如何知道方法有緩存了,再繼續往下執行:

      imageng

      這時候執行完 test02, _mask的值從 3 變成了 7 ,說明散列表 bucket_t 做了擴容操作。在這里bucket_t 元素需要 _mask 個元素,所以最終 bucket_t 從原有的3個元素進行了 2倍 擴容。

      在看下方法是否進行緩存:

      imageng

      可以看見當執行完 [stu test02] 時,數據做了擴容,并且擴容的數據使用(null) 進行填充。

      在看個事例:

      imageng

      在執行 [stu test] 之前;其實bucket_t 就3個元素,并且存入了 init 方法;

      imageng

      當執行完 [stu test] 之后;就存入 test 方法。

      但是注意的地方:它在擴容時對之前的緩存進行清除。

      image.png

      通過查看源碼,我們知道了它如何進行清除操作,

      imageng

      當執行完 [stu test02];[stu test03]; 之后,它先將緩存清空;這時候 init , test 方法被清空,bucket_t擴容完在存儲:test02test03 方法。

      那問題又來了,它是如何快速定位到方法的,然后執行的?接下來看看代碼:

      imagepng

      可以清楚看見,當我使用 @selector(test03)&stu_cache._mask 就可以得到下標,然后再從 bucket_t 拿到方法。

      到這里 class結構,類的方法緩存到此結束了,從上面也可以思考下:如果自己去實現散列表數組,是不是思路就跟清晰了。

      謝謝大家!青山不改,綠水長流。后會有期!

      posted @ 2024-03-24 13:51  一眼萬年的星空  閱讀(197)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲欧美激情在线一区| 日韩成人精品一区二区三区| 亚洲中文字幕一区二区| 韩国三级+mp4| 亚洲精品国产精品乱码不| 久久精品国产亚洲精品| 欧美性猛交xxxx黑人猛交| 人妻熟妇乱又伦精品无码专区| 91久久国产成人免费观看| 乱色老熟妇一区二区三区| 国产高清视频在线播放www色| 国产亚洲视频在线播放香蕉| 精品无码一区在线观看| 粉嫩一区二区三区精品视频| 91久久精品美女高潮不断| 日韩中文字幕精品人妻| 天堂网亚洲综合在线| 国产精品色内内在线播放| 灵山县| 国产高清视频一区二区三区| 国产精品自拍一二三四区| 欧美性猛交xxxx乱大交极品| 内射干少妇亚洲69xxx| 极品蜜桃臀一区二区av| 9久久伊人精品综合| 26uuu另类亚洲欧美日本| 亚洲av无码专区在线亚| 蜜桃臀无码AV在线观看| 欧美黑人添添高潮a片www| 91久久亚洲综合精品成人| 无码国模国产在线观看免费| 99精品国产丝袜在线拍国语| 国产欧美久久一区二区| 亚洲激情一区二区三区视频| 欧美牲交a欧美牲交aⅴ免费真| 祥云县| 国产熟女老阿姨毛片看爽爽| 国产精品一区二区中文| 18女下面流水不遮图| 无码乱人伦一区二区亚洲| 人妻蜜臀久久av不卡|