super(二) 以及記憶體分布

  • 2019 年 10 月 8 日
  • 筆記

一、練習

定義一個Person類

@interface Person : NSObject  @property (nonatomic, strong) NSString *age;  - (void)test;  @end    @implementation Person  - (void)test{      NSLog(@"age = %@",self.age);  }

ViewController 繼承自UIViewController

在ViewController 書寫以下程式碼。問是否能編譯通過,如果可以輸出什麼是什麼?

- (void)viewDidLoad {      [super viewDidLoad];      // Do any additional setup after loading the view.        id cls = [Person class];      void * temp = &cls;      [(__bridge id)temp test];          Person *per = [[Person alloc] init];      [per test];    }

運行結果

age = <ViewController: 0x7fb25241d9c0>  age = (null)

分析

1. [super viewDidLoad]

product -> Preform Action-> assemble 查看中間層程式碼

.loc	1 18 5 prologue_end     ## /Users/iOS/Desktop/Demo/Demo/ViewController.m:18:5  movq	-8(%rbp), %rsi  movq	%rsi, -32(%rbp)  movq	L_OBJC_CLASSLIST_SUP_REFS_$_(%rip), %rsi  movq	%rsi, -24(%rbp)  movq	L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi  leaq	-32(%rbp), %rdi  callq	_objc_msgSendSuper2

其實super 底層實現的是_objc_msgSendSuper2 而不是上一篇文章的 _objc_msgSendSuper 兩個還是有區別的。我們先看一下_objc_msgSendSuper傳參

// rewrite-objc 結果  _objc_msgSendSuper(  	struct {  		id self  		Class superClass    },@selector(viewDidLoad)  )  // 底層實現 彙編結果    _objc_msgSendSuper2(  	struct {  		id receiver  		Class current_class    },@selector(viewDidLoad)  )

通過assemble生成的中間程式碼可以看出調用的是_objc_msgSendSuper2 在源碼中也有對_objc_msgSendSuper2的參數介紹

	ENTRY _objc_msgSendSuper2  	UNWIND _objc_msgSendSuper2, NoFrame    	ldp	p0, p16, [x0]		// p0 = real receiver, p16 = class  	ldr	p16, [x16, #SUPERCLASS]	// p16 = class->superclass  	CacheLookup NORMAL    	END_ENTRY _objc_msgSendSuper2

從源碼中 可以看到 傳入參數為第一個為接收者(self) 第二個參數為 [current class]

ldr	p16, [x16, #SUPERCLASS]	// p16 = class->superclass

在上面這句程式碼中 獲取到當前class 的superclass

2.viewDidLoad中的記憶體分配

我們先看圖下面區域

藍色區域為person的實例變數。per指向person實例變數

而我們的對象方法存儲在class中,所以通過指針找到per ->isa -> class ->方法列表 ->具體方法

我們再看圖上面區域

藍色區域為cls變數 temp指向cls cls指向person class

從圖中我們可以看出,兩個在記憶體結構幾乎一致。

我們知道 c中是通過地址與指針找到對象。可以觸發person的對象方法

既然是地址尋找。那當我們調用age屬性的時候,正常走的是person實例變數的地址。而在temp中,這個地址為

self,所以temp中調用的age為viewcontroller 因為他們在相同的記憶體位置。

二、從源碼的角度看isMemberOfClass和isKindOfClass

+ (BOOL)isMemberOfClass:(Class)cls {    // 當類傳進來的時候 獲取的是mate-class      return object_getClass((id)self) == cls;  }    - (BOOL)isMemberOfClass:(Class)cls {      return [self class] == cls;  }    + (BOOL)isKindOfClass:(Class)cls {      for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {  				// 循環遍歷父類 直到nil 或者相等          if (tcls == cls) return YES;      }      return NO;  }    - (BOOL)isKindOfClass:(Class)cls {      for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {          if (tcls == cls) return YES;      }      return NO;  }

結論

當類方法object_getClass(self) == mate-class

  • isMemberOfClass 當前對象的class是等於傳進來的class
  • isKindOfClass 是否為傳進來類的類或者子類