iOS[super class]和[self class]

大兵布萊恩特0409發表於2018-07-12

最近小編所在公司招 iOS 開發職位,小編也出了幾道面試題考察下候選人的 iOS 開發水平,其中有一道題如下:

@implementation Student : Person

- (instancetype)init
{
	self = [super init];
	if (self) {
	
	
		id obj1 = [self class];
		id obj2 = [super class];
		NSLog(@"%@",obj1);
		NSLog(@"%@",obj2);

	}
	return self;
}
@end
複製程式碼

大部分候選人回答的 [self class ]輸出 Student , [Super class]輸出 Person, 只有少部分候選人回答都是輸出 Student ,當然至於為什麼輸出結果都是 Student, 很少有能回答出來的.

接下來小編通過將Student.m 轉換成 Cpp 檔案,帶大家一塊去看看 [self class] 和 [super class] 背後究竟做了那些事情.

通過終端講Student.m 轉成Student.cpp 檔案

image.png

2 找到 Student 的 init 方法 分析程式碼

static instancetype _I_Student_init(Student * self, SEL _cmd) {
 self = ((Student *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("init"));
 if (self) {


  id obj1 = ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"));
  id obj2 = ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Student"))}, sel_registerName("class"));
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_0,obj1);
  NSLog((NSString *)&__NSConstantStringImpl__var_folders_hj_pwgsq9614nb0vq4zd315tcx80000gn_T_Student_e7cbc1_mi_1,obj2);

 }
 return self;
}

複製程式碼

由此可見 當我們呼叫[self class] 時候實際上編譯器最終給我們轉成 objc_msgSend(self,@selector(class)) , 訊息的接收者是當前所在類的例項物件 , 這個時候就會去 self 所在類 Student 去查詢 class 方法 , 如果當前類 Student 沒有 class 會向Student 父類 Person 類找 class 方法, 如果 Person 類也沒有找到 class 方法,最終會找到最頂級父類 NSObject 的 class 方法, 最終找到 NSObject 的 class 方法 ,並呼叫了object_getClass(self) ,由於訊息接收者是 self 當前類例項物件, 所以最終 [self class]輸出 Student

那麼為什麼明明呼叫了 super 這個關鍵字 返回的[super class] 還是 Student 呢 ?

通過上邊程式碼可知 , [super class] 最終編譯器轉化成了 objc_msgSendSuper(struct objc_super *,SEL) ,其中

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;

    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
#endif

複製程式碼

objc_super 是一個結構體,內部有一個 receiver 例項,和一 個 Class super_class,指向了當前類的父類 Class ,通過這個父類可以直接的從父類裡邊開始查詢方法,由於訊息接收者還是當前類的例項物件 self, 最終如果在父類中沒有找到class 這個方法,會在 Person 類的父類 NSObject 中去查詢 class 方法,由於 NSObject 的 class 方法,呼叫的是 object_getClass(self) ,所以最終訊息接收者為 student 例項物件,所以返回的還是 Student .

當然我們知道了[ super class] 最終編譯後程式碼樣子,也可以自己手動去呼叫 objc_msgSendSuper 方法


- (instancetype)init
{
	self = [super init];
	if (self) {
	
	
		id obj1 = [self class];
		id obj2 = [super class];
		NSLog(@"%@",obj1);
		NSLog(@"%@",obj2);

		struct objc_super1 {
		    __unsafe_unretained id receiver;
			Class superClass;
		};
		struct objc_super1 obj_super = {self,class_getSuperclass(object_getClass(self))};
		id obj3 = objc_msgSendSuper(&obj_super,@selector(class));

	}
	return self;
}

複製程式碼

obj3輸出的結果和直接呼叫 [super class]結果是一模一樣的,剛才我們假設的情況都是 super Person 沒有 class 方法,如果 Person 重寫了 class 方法呢,將會怎麼樣?

image.png

當我們在 Student init 方法中呼叫 [super class] 時候 ,它首先會到 Person 類中查詢 class 方法 ,當它發現了 Person 實現了 class 方法,就會呼叫[person class] 方法, object_getClass 這個時候 object_getClass(self), 這個 self 是 Student 的例項物件,就是訊息接收者,所以即使重寫了 Person 的 class 方法 ,依然返回的還是 Student ,除非來個極端的 把 class 方法 實現 返回個 nil , 這樣最終呼叫[super class]結果才會返回nil

KVO巧妙的利用了子類 重寫了 class 方法 ,讓我們誤以為 [person class] 還是當前類 Person ,而不是動態建立的子類 NSKVONotifily_Person 這個子類 , 就是讓子類返回了父類的 Class ,所以呼叫 [person class] 返回的還是 Person 從而對開發者隱藏了 NSKVONotifily_Person子類的存在

- (Class)class {
	return class_getSuperclass(object_getClass(self));
}
複製程式碼

相關文章