深刻理解Objective-C中實例、類對象、元類對象之間的關係

實例、類對象、元類對象之間的關係能夠用下面這張經典的圖來展現:bash

image

總結:數據結構

  • 實例的isa指向類對象
  • 類對象的isa指向元類對象
  • 元類對象的isa指針指向根元類對象
  • 根元類的父類是根類對象

根元類的父類是根類對象意味着什麼呢?

咱們知道若是調用類方法,會沿着元類對象的繼承鏈依次向上查找方法的實現。ui

由於跟元類的父類是根類對象,因此若是在跟元類中沒法查找到該方法的實現,會到根類對象中去查找。spa

而根類對象中的方法都是實例方法。指針

因此這意味着,若是一個類的類方法沒有被實現,就會去調用它的根類(NSObject)中的同名實例方法。code

咱們用代碼驗證一下。cdn

NSObject新建一個Category,並添加一個名爲foo的實例方法。方法的實現只是簡單打印foo字符串對象

//NSObject+Foo.h

#import <Foundation/Foundation.h>
@interface NSObject (Foo)
- (void)foo;
@end


//NSObject+Foo.m

#import "NSObject+Foo.h"
@implementation NSObject (Foo)
- (void)foo {
    NSLog(@"foo foo");
}
@end
複製代碼

新建一個UIView的子類,並在這個子類的.h文件中聲明一個名爲foo類方法,但在.m文件中並不實現它。blog

// CView.h

#import <UIKit/UIKit.h>
@interface CView : UIView
+ (void)foo;
@end

// CView.m
#import "CView.h"
@implementation CView

@end

複製代碼

而後調用這個類方法[CView foo];繼承

按理說咱們沒有實現+ (void)foo;程序應該崩潰。可是,實際上會調用NSObject的Category中的實例方法- (void)foo,打印出"foo foo",很是神奇

使用super關鍵字調用方法的時候,發生了什麼

若是上面提到的CView有這樣一個初始化方法:

#import "CView.h"

@implementation CView
- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        NSLog(@"%@",NSStringFromClass([self class]));
        NSLog(@"%@",NSStringFromClass([super class]));
    }
    return self;
}
@end
複製代碼

問:若是調用這個初始化方法,打印的結果是什麼?

要解答這個問題,咱們須要瞭解消息傳遞的過程?

咱們知道,在OC中方法調用[obj method]會被編譯器編譯爲objc_msgSend,它的定義爲objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)

前兩個參數是固定的,分別是消息的接受者和方法選擇器。

那麼super關鍵字調用方法會被轉換爲objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...),第一個參數爲objc_super指針

objc_super是下面的數據結構:

/// 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 */
}

複製代碼

其中的receiver是類的一個實例,也就是self。

也就是說,最終消息的接受者仍是self這個實例。只不過,objc_msgSendSuper查找方法實現的過程,是從self的父類對象開始的。

可是class方法是在根類NSObject中定義的,因此不管是[self class]仍是[super class]都會調用NSObject中的實現,並且消息的接收者也是同樣的,都是self這個實例。因此打印結構也是同樣的,都打印self的類名。

相關文章
相關標籤/搜索