­

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 是否为传进来类的类或者子类