Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于 “super关键字” 的一点疑问(Some thoughts on the implementation of self.super() in JSPatch) #823

Open
ShannonChenCHN opened this issue May 8, 2018 · 0 comments

Comments

@ShannonChenCHN
Copy link

ShannonChenCHN commented May 8, 2018

感谢 bang 哥分享了一个这么棒的框架!👏

在读 JSPatch-实现原理详解 时,看到有这样一段话:

OC 里 super 是一个关键字,无法通过动态方法拿到 super,那么 JSPatch 的 super 是怎么实现的?实际上调用 super 的方法,OC 做的事是调用父类的某个方法,并把当前对象当成 self 传入父类方法,我们只要模拟它这个过程就行了。

但是,实际上 Objective-C 中对于 super 的实现并不是调用当前对象或者类对象(也就是 receiver)的父类的某个方法,而是调用 [super xxx] 代码所在类的父类的方法。

我的理解是,[self xxx] 要调用的实现是在运行时动态决定的,而 [super xxx] 要调用的实现是编译时就确定了的。从下面用 clang 重写出来的 cpp 代码中也可以看出来,这其实是因为 objc_msgSendSuper 函数的第一个参数 objc_super 结构体中的 receiver 是通过接收方法中的 self 参数得来的,所以动态决定的,而 objc_super->superClass 是通过 class_getSuperclass(objc_getClass("Parent")) 得到的,所以是静态的,在编译时就确定了的。


以下面的 Objective-C 示例代码为例,我测试了一下:

@interface Grandparent : NSObject

- (void) One;

@end

@implementation Grandparent

- (void) One { NSLog(@"Grandparent One\n"); }

@end

@interface Parent : Grandparent

- (void) One;
- (void) Two;

@end

@implementation Parent

- (void) One { NSLog(@"Parent One\n"); }

- (void) Two
{
    [self One];                 // will call One based on the calling object
    [super One];                // will call One based on the defining object - Parent in this case so will Grandparent's One
}

@end

@interface Child : Parent

- (void) One;

@end

@implementation Child

- (void) One { NSLog(@"Child One\n"); }

@end

void testSelfSuper() {
    Child *c = [Child new];
    [c Two];                            // will call the Two inherited from Parent
    
    Parent *p = [Parent new];
    [p Two];                            // will call Parent's Two
    
}

输出的结果是:

Child One
Grandparent One
Parent One
Grandparent One

用 clang 讲上面的代码重写成 C++ 代码后,Parent 中的 [super One]; 会被转换成下面的代码:

...
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Parent"))}, sel_registerName("One"));
...

然后我再通过 JSPatch 使用 JavaScript 实现上面的示例时,就出现了不一样的结果:

require('NSObject');

defineClass('Grandparent: NSObject', {
  one: function() {
    _OC_log('Grandparent One\n');
  }
});


defineClass('Parent: Grandparent', {
  one: function() {
    _OC_log('Parent One\n');
  },

  two: function() {
    self.one();
    self.super().one();
  }
});


defineClass('Child: Parent', {
  one: function() {
    _OC_log('Child One\n');
  }
});

console 打印结果:

Child One
Parent One
Parent One
Grandparent One

所以,我们是否可以得出这样的结论:JSPatch 中的 self.super() 并不能完全等同于 Objective-C 中的 super 来使用?

@ShannonChenCHN ShannonChenCHN changed the title 关于 “super关键字” 的一点疑问(Some thoughts on the implementation of self in JSPatch) 关于 “super关键字” 的一点疑问(Some thoughts on the implementation of self.super() in JSPatch) May 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant