3. OC語法-分類Category

一、 Category的底層結構

  • 定義在objc-runtime-new.h中
    在這裏插入圖片描述

二、 Category的加載處理過程

2.1 加載順序

  1. 通過Runtime加載某個類的所有Category數據
  2. 把所有Category的方法、屬性、協議數據,合併到一個大數組中
    後面參與編譯的Category數據,會在數組的前面
  3. 將合併後的分類數據(方法、屬性、協議),插入到類原來數據的前面

2.2 源碼解讀順序

  • objc-os.mm

    • _objc_init
    • map_images
    • map_images_nolock
  • objc-runtime-new.mm

    • _read_images
    • remethodizeClass
    • attachCategories
    • attachLists
    • realloc、memmove、 memcpy

三、+ load方法

  • +load方法會在runtime加載類、分類時調用

  • 每個類、分類的+load,在程序運行過程中只調用一次

  • 調用順序

  1. 先調用類的+load
    • 按照編譯先後順序調用(先編譯,先調用)
    • 調用子類的+load之前會先調用父類的+load
  2. 再調用分類的+load
    • 按照編譯先後順序調用(先編譯,先調用)
  • objc4源碼解讀過程:objc-os.mm

    • _objc_init

    • load_images

    • prepare_load_methods

      • schedule_class_load
      • add_class_to_loadable_list
      • add_category_to_loadable_list
    • call_load_methods

      • call_class_loads
      • call_category_loads
      • (*load_method)(cls, SEL_load)
  • +load方法是根據方法地址直接調用,並不是經過 objc_msgSend函數調用

四、+initialize方法

  • +initialize方法會在類第一次接收到消息時調用

  • 調用順序

    • 先調用父類的+initialize, 再調用子類的+initialize
    • (先初始化父類, 再初始化子類, 每個類只會初始化1次)
  • objc4源碼解讀過程

    • objc-msg-arm64.s
      • objc_msgSend
    • objc-runtime-new.mm
      • class_getInstanceMethod
      • lookUpImpOrNil
      • lookUpImpOrForward
      • _class_initialize
      • callInitialize
      • objc_msgSend(cls, SEL_initialize)
  • initialize內部實現邏輯

// 在子類中, 會判斷父類是否已經初始化過, 沒有會調用一次父類的初始化
objc_msgSend([MJPerson class], @selector(initialize));
objc_msgSend([MJStudent class], @selector(initialize));


// MJPerson (Test2) +initialize
objc_msgSend([MJPerson class], @selector(initialize));
// MJPerson (Test2) +initialize
objc_msgSend([MJStudent class], @selector(initialize));
// MJPerson (Test2) +initialize
objc_msgSend([MJTeacher class], @selector(initialize));
  • +initialize和+load的很大區別是, +initialize是通過objc_msgSend進行調用的, 所以有以下特點
    • 如果子類沒有實現+initialize, 會調用父類的+initialize(所以父類的+initialize可能會被調用多次)
    • 如果分類實現了+initialize, 就覆蓋類本身的+initialize調用

五、總結

load、initialize方法的區別什麼?

  1. 調用方式
    1> load是根據函數地址直接調用
    2> initialize是通過objc_msgSend調用

  2. 調用時刻
    1> load是runtime加載類、分類的時候調用(只會調用1次)
    2> initialize是類第一次接收到消息的時候調用,每一個類只會initialize一次(父類的initialize方法可能會被調用多次)

load、initialize的調用順序?

  1. load
    1> 先調用類的load
    a) 先編譯的類,優先調用load
    b) 調用子類的load之前,會先調用父類的load

    2> 再調用分類的load
    a) 先編譯的分類,優先調用load

  2. initialize
    1> 先初始化父類
    2> 再初始化子類(可能最終調用的是父類的initialize方法)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章