self和super到底怎么用?

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/michaelscofielddong/article/details/47807099

开发过程中遇到一个问题.

问题简化描述如下:

有一个UIView的子类(CTestLevel),实现了init方法和initWithFrame方法,只调用了CTestLevel的init,init中只调用了[super init] , 可为什么当前类的initWithFrame被调用了?不应该是调用父类吗?怎么调用了子类的initWithFrame?

如下:

  1. 初始化一个对象
CTestLevel  *testObj = [CTestLevel  alloc] init];
@implementation CTestLevel

- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        NSLog(@"initWithFrame");
    }

    return self;
}

-(instancetype)init
{
    self = [super init];
    if (self)
    {
        NSLog(@"init");
    }

    return self;
}
@end

2.设置断点,查看调用关系
调用关系

3 查看堆栈
堆栈

查看调用关系和堆栈,
testObjc调用了init方法,然后调用了UIView的init的方法,这没有问题,
因为CTestLevel的init方法调用了[super init];

  1. 堆栈中最上层为什么又调用了CTestLevel 的initWithFrame方法呢?
  2. initWithFrame是UIView的指定初始化方法,按道理应该调用UIView的initWithFrame方法啊,为什么调用了CTestLevel的initWithFrame的初始化方法?
  3. 因为子类重写了initWithFrame,所以调用子类的initWithFrame?如果不重写就调用UIView的initWithFrame?
  4. super self 都是什么意思?

这都是怎么一个逻辑,具体是怎么实现的?

  1. 先说明Objective-C的类图结构:

类图结构
其中实心箭头表示继承关系,CTestLevel继承自UIView,UIView继承自UIResponder, …一直继承到NSObject.NSObject的父类为nil, 所有类变量到最终的父类都是nil.
虚线箭头表示所属关系:testObj属于CTestLevel类的对象,CTestLevel类属于CTestLevel元类的对象。所有的元类对象属于NSObject元类对象,NSObject元类对象属于自身.

  1. 代码说明Objective-C类的结构
    Objective-C中所有的东西都是对象,即使是类,它也是一种对象。
    testObj对象的如下表示:
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

objc_object 表示一个对象,其中的isa指向CTestLevel类

Class的结构体定义如下:

typedef struct objc_class *Class;

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

其中isa指向CTestLevel元类,super_class指向其父类UIView,ivars存放变量,methodLists方法列表,cache缓存方法,protocols类所遵循的协议。

  1. self和super的含义,在Objective-C中self表示当前对象(就是这个testObj对象),类似于C++里面的this指针;
    super其实是一个编译器指示符,在Runtime运行期这个关键词是不存在的。只是辅助编译器做了一些处理罢了,类似语法糖的东西。

  2. 如何向对象发消息
    testObj对象可以调用(init,initWithFrame, setHidden等方法),具体是怎么做到的?
    例如:
    当[testObj init]这样发送消息的时候,会转化为 id objc_msgSend(id theReceiver, SEL theSelector, …)来调用。其中theReceiver表示testObj对象,theSelector表示init方法. …表示参数,这里参数为空。
    简写成如下这样:obj_msgSend(testObj,init,”“)
    如果是调用哪个initWithFrame的话,大体如下:
    obj_msg_Send(testObj,@Selector(initWithFrame),frame)

那调用[super init]的时候,没有testObj这个对象了?怎么办?
[super init]会转化成如下的方法
id objc_msgSendSuper(struct objc_super *super, SEL op, …)
一个objc_super,它具体是什么呢?结构体定义如下:

struct objc_super {
    id receiver;
   Class superClass;
};

里面也有一个receiver,这个receiver就是消息的接受者,这里是self,就是这个testObj对象!
superClass: self的父类,也就是UIView.
op:表示方法;这里就是init.
一句话:去父类里面找init方法,然后给testObj发消息。对,用当前对象,调用父类的方法。

self和super的区别,就是去哪里找方法的问题,接受消息的对象还是那个对象。

举个例子说明:
类的继承关系:
类的继承关系

CTestLevel_3继承自CTestLevel_2,CTestLevel_2继承自CTestLevel_1,CTestLevel_1继承自UIView.
testObj是一个CTestLevel_3类型的对象。
CTestLevel_3实现了一个testLevel3方法,方法直接调用 : [super testLevel2];
CTestLevel_2实现了一个testLevel2方法,方法打印输出:NSStringFromClass([self class]);

因为testObj是一个CTestLevel_3的对象类型,所以输出结果[self class]是一个CTestLevel_3类型。等价于给CTestLevel_3类型的对象,发送父类的testLevel2消息。

经常遇到下面的问题:

    NSLog(@"%@\n",self); //CTestLevel_3
    NSLog(@"%@\n",[self class]);//CTestLevel_3
    NSLog(@"%@\n",[self superclass]);//CTestLevel_2
    NSLog(@"%@\n",[[self class] superclass]);//CTestLevel2 class
    NSLog(@"%@\n",[super class]);//CTestLevel3
    NSLog(@"%@\n",[super superclass]);//CTestLevel2

完。

展开阅读全文

super和this到底是什么

11-16

回过头来再看的时候真的已经不知道它们分别是什么东西了,JLS上也就只是说它们是关键字。但是它们真正到底是什么呢?rn对于super,《The Java Programming Language》上这样说:In field access and method invocation, super acts as a reference to the current object as an instance of its superclass.可以理解为在需要访问the hidden field和进行父类方法调用的时候,super表现为指向作为父类的一个实例的当前对象的一个引用。所以super只是act as a reference,但不是reference,对吗?我们也不可以将super赋给一个父类变量,比如在子类中:Super s = super;(报错)。当我们在构造方法中使用super()或其他形式的时候super又是什么呢?rn对于this,JLS上这样表述:When used as a primary expression, the keyword this denotes a value that is a reference to the object for which the instance method was invoked (§15.12), or to the object being constructed.这里:this也只是代表一个引用的值,还是没说this除了是keyword外还是什么,但是又说的this的类型。反正搞得一团乱,下面的代码输出的结果我也不理解:rn[code=Java]rnclass Super rnrnrnclass SubClass extends Superrn public void foo() rn System.out.println(this.getClass());rn System.out.println(super.getClass());rn rnrn[/code]rn希望大家帮忙解惑,说说它们是什么。怎么使用它们就不用说了,所有的书上都差不多教我们怎么用了。谢谢~ 论坛

const 到底怎么用?

08-16

unit Unit1;rnrninterface rnrn 记住在uses部分中包括 ShellAPI rnuses rnWindows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, rnShellAPI, StdCtrls; rnrn自定义消息,当小图标捕捉到鼠标事件时Windows向回调函数发送此消息 rnconst MY_MESSAGE = WM_USER + 100; rnrntype rnTForm1 = class(TForm) rnprocedure FormCreate(Sender: TObject); rnprocedure FormClose(Sender: TObject; var Action: TCloseAction); rnprocedure FormPaint(Sender: TObject); rnprivate rnprocedure OnIconNotify(var Message: TMessage); rnmessage MY_MESSAGE; rnpublic rn Public declarations rnend;rnrnvar rnForm1: TForm1; rnrnimplementation rnrn$R *.DFM rnrnrn当小图标捕捉到鼠标事件时进入此过程rnprocedure TForm1.OnIconNotify(var Message: TMessage);rnconstrn Busy : Boolean = false;rnbeginrn if not Busy thenrn beginrn Busy := true;rn if Message.LParam=WM_LBUTTONDOWN thenrn if Application.MessageBox('Are you sure','Exit', MB_YESNO)=IDYES then Close;rn Busy := false;rn end;rnend;rnrn当主Form建立时通知Windows加入小图标 rnprocedure TForm1.FormCreate(Sender : TObject);rnvar rnnid: TNotifyIconData; rnbegin rnnid.cbSize := sizeof(nid); // nid变量的字节数 rnnid.Wnd := Handle; // 主窗口句柄 rnnid.uID := -1; // 内部标识,可设为任意数 rnnid.hIcon := Application.Icon.Handle; // 要加入的图标句柄,可任意指? rnnid.hIcon := Application.Icon.Handle; // 要加入的图标句柄,可任意指? rnrnnid.szTip := 'This is a test application'; // 提示字符串 rnnid.uCallbackMessage := MY_MESSAGE; // 回调函数消息 rnnid.uFlags := NIF_ICON or NIF_TIP or NIF_MESSAGE; // 指明哪些字段有? rnrnif not Shell_NotifyIcon(NIM_ADD, @nid) then begin rnShowMessage('Failed!'); rnApplication.Terminate; rnend; rn将程序的窗口样式设为TOOL窗口,可避免在任务条上出现 rnSetWindowLong(Application.Handle, GWL_EXSTYLE, WS_EX_TOOLWINDOW); rnend; rnrn程序被关闭时通知Windows去掉小图标 rnprocedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); rnvar rnnid: TNotifyIconData; rnbegin rnnid.cbSize := sizeof(nid); // nid变量的字节数 rnnid.cbSize := sizeof(nid); // nid变量的字节数 rnnid.uID := -1; //内部标识,与加入小图标时的数一致rnnid.Wnd := Handle; //主窗口句柄 rnShell_NotifyIcon(NIM_DELETE, @nid); //去掉小图标 rnShell_NotifyIcon(NIM_DELETE, @nid); //去掉小图标 rnend; rnrn主窗口初始化完毕并显示时将激活Paint重画事件,此时将主窗口隐藏 rnprocedure TForm1.FormPaint(Sender: TObject); rnbegin rnHide; rnend; rnrnend. rnrnrnrn这个是我看到的,试试看不行。好象const是不能在被赋值的吧。但我看到过很多都这么写:rnrn比如rnprocedure TForm1.OnIconNotify(var Message: TMessage);rnconstrn Busy : Boolean = false;rnbeginrn if not Busy thenrn beginrn Busy := true; // **** Left side cannot be assigned to****rn if Message.LParam=WM_LBUTTONDOWN thenrn if Application.MessageBox('Are you sure','Exit', MB_YESNO)=IDYES then Close;rn Busy := false;rn end;rnend;rnrnBusy是const定义的,为什么下面还能赋值呢?rn不过我在delphi6里编译通不过。rnLeft side cannot be assigned tornrn还请大家帮我看看。 论坛

没有更多推荐了,返回首页