黑马程序员---关于super和self关键字

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

 

2015430星期四

上午12:48

  • 类是蓝图,但是在oc中类也有自己的方法。这些方法独立于对象。
  • 方法中可以用self和super指定调用者和调用者的父类(或对象),使用它们可以访问自己和父亲的成员。

注意:如果调用者是一个对象,那么super也是对象,那如果想调用父类方法呢?

oc中没有这种语法,但是因为编写代码时知道父类的名称,所以可以直接写上父类的名称。[父类  类方法]

  • 继承主要是为了让子类可以拥有父类公共的成员(包括方法)
  • 因为oc中子类的同名方法可以覆盖父类的方法,这叫重写。

我觉得继承是一种框架,像树,可以用super访问父对象。儿子拥有父亲,爷爷。。的所有。名副其实:继承。越到树枝叶部分,功能越多,而越到根部,是公共部分,是基本方法,基本属性。随着编写,树可以越长越大。

  • 还有我注意到,到我们创建一个子类对象时,因为可以访问父类对象的成员,那么必定内存中也创建了父类的对象,可是因为我们不知道他的指针,所以没办法直接访问他。只能通过我们创建的子类对象访问父类对象。
  • 所以我们每次创建一个子类对象时,实际上内存中不止是从类(蓝图)中拷贝了子类,还有他的所有祖宗。(估计只是拷贝成员变量)
  • 我在看视频时有个疑问:就是super是指调用者的父亲呢,还是找到的方法所属类(或对象)的父亲。可能有点绕口,我写了个代码验证,具体看代码。
  •  

#import<Foundation/Foundation.h>

/****Animal****/

@interface Animal:NSObject

- (void)test;

@end

@implementation Animal

- (void) test

{

    NSLog(@"write by Animal");

}

@end

/*****Person*****/

@interface Person:Animal

{

    int _age;

}

- (void)baogao;

- (void) test;

@end

@implementation Person

- (void) test

{

    NSLog(@"write by Person");

}

- (void)baogao

{

/*找到person类中的baogao*/

    NSLog(@"I'm a person");//print

    [super test];/*这里是实验的重点:这里的super是指person的父类,还是student的父类*/

}

@end

 

/************Student***   studentperson的子类,person又是animal的子类*/

@interface Student:Person

 

@end

@implementation Student

 

 

@end

 

int main()

{

    Student *s=[Student new];

    [s baogao];/*因为student对象s没有baogao方法,所以要到父类person找*/

    return 0;

}

  • 我是这么想的:self代表调用者,那么super就是调用者的父亲啦。
  • 因为父亲可能是对象,也可能是类,所以我就说是父亲。

 

我从s调用baogao来到- (void)baogao,注意因为student里没有baogao,所以去父对象里找到了baogao,然后

- (void)baogao

{

    NSLog(@"I'ma person");

   [super test];

   

}

,请问这个super指的是原始调用者s(就是student类对象)的父亲  现在的调用者person对象的父亲。

输出是:

2015-04-3002:01:26.678 a.out[908:56110] I'm a person

2015-04-3002:01:26.679 a.out[908:56110] write by Animal

这与我原来的想法背道而驰。

 

我查了一段介绍super

 

 

Sending Messages

When it encounters a methodinvocation, the compiler might generate a call to any of several functions toperform the actual message dispatch, depending on the receiver, the returnvalue, and the arguments. You can use these functions to dynamically invokemethods from your own plain C code, or to use argument forms not permitted byNSObject’s perform… methods. These functions are declared in/usr/include/objc/objc-runtime.h.

    但我们方法调用时,编译器会根据我们的调用的方法,调用以下四个函数中的一个来,转发消息给接收者(对象)。可以在c语言中调用这些函数来实现objectc方法调用。

objc_msgSend sends amessage with a simple return value to an instance of a class.平常的会发这个

objc_msgSend_stret sends amessage with a data-structure return value to an instance of这个函数的参数会带一个结构指针,这个指针是给对象方法来操作这个结构的,结构用来放返回值。

a class.

objc_msgSendSuper sends amessage with a simple return value to the superclass of an instance of a class.使用【super 消息】关键字就会用这个

objc_msgSendSuper_stretsends a message with a data-structure return value to the superclass of aninstance of a class.同上

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Sends a message with a simple return value to an instance of a class.

Declaration

OBJECTIVE-C

id objc_msgSend ( id self, SEL op, ... );

Parameters
self

A pointer that points to the instance of the class that is to receive the message.指向接受方法的对象

op

The selector of the method that handles the message.   SEL

...

A variable argument list containing the arguments to the method.参数

Return Value

The return value of the method.



Sends a message with a simple return value to the superclass of an instance of a class.

Declaration

OBJECTIVE-C

id objc_msgSendSuper ( struct objc_super *super, SEL op, ... );

Parameters
super

A pointer to an objc_super data structure. Pass values identifying the context the message was sent to, including the instance of the class that is to receive the message and the superclass at which to start searching for the method implementation.  这个是个结构,同样指出接受对象,和父类的指针

op

A pointer of type SEL. Pass the selector of the method that will handle the message.

...

A variable argument list containing the arguments to the method.

Return Value

The return value of the method identified by op.

Specifies the superclass of an instance.

Declaration

SWIFT

struct objc_super { var receiver: AnyObjectvar super_class: AnyClassinit() init(receiverreceiverAnyObject!, super_class super_classAnyClass!) }

OBJECTIVE-C

struct objc_super { id receiver; Class class; };

Fields
receiver

A pointer of type id. Specifies an instance of a class.

class

A pointer to an Class data structure. Specifies the particular superclass of the instance to message.

Discussion

The compiler generates an objc_super data structure when it encounters the super keyword as the receiver of a message. It specifies the class definition of the particular superclass that should be messaged.

使用[p test]时,编译器会把他转换成调用 objc_msgSend,参数有 接收对象指针, 消息SEL ,参数

使用[super test]时,调用objc_msgSendSuper,第一参数是一个结构,包含接收对象指针, 还有 父类指针 ,第二个参数是sel,然后是参数。


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

<strong><em><span style="color:#ff0000;">实验1</span></em></strong>
Person * p = [Person new];
    [p test];
    objc_msgSend(p,@selector(test));

2015-05-20 11:27:50.466 super[669:46549] ts ok

2015-05-20 11:27:50.467 super[669:46549] ts ok

可以成功调用方法啦!

实验2 

    [p test2:2];
    
    objc_msgSend(p,@selector(test2:),2);
成功传参数

2015-05-20 11:58:38.408 super[763:56220] 2

2015-05-20 11:58:38.410 super[763:56220] 2


实验3

struct objc_super2
{
    __unsafe_unretained id reciver;
    Class super_class;
};
int main(int argc, const char * argv[]) {
    Person * p = [Person new];
    [p test2:2];
    
    objc_msgSend(p,@selector(test2:),2);
    
    
    Student * s = [Student new];
    struct objc_super2 a= {s , [Person class]};
    
    
    objc_msgSendSuper(&a,@selector(test));
    return 0;
}

2015-05-20 12:08:03.031 super[814:59477] 2

2015-05-20 12:08:03.033 super[814:59477] 2

2015-05-20 12:08:03.033 super[814:59477] ts ok


引用

第11条:理解objc_msgSend的作用

在对象上调用方法是Objective-C中经常使用的功能。用Objective-C的术语来说,这叫做“传递消息”(pass a message)。消息有“名称”(name)或“选择子”(selector),可以接受参数,而且可能还有返回值。

由于Objective-C是C的超集,所以最好先理解C语言的函数调用方式。C语言使用“静态绑定”(static binding),也就是说,在编译期就能决定运行时所应调用的函数。以下列代码为例:
 

  
  
  1. #import <stdio.h> 
  2.  
  3. void printHello() {  
  4.     printf("Hello, world!\n");  
  5. }  
  6. void printGoodbye() {  
  7.     printf("Goodbye, world!\n");  
  8. }  
  9.  
  10. void doTheThing(int type) {  
  11.     if (type == 0) {  
  12.         printHello();  
  13.     } else {  
  14.         printGoodbye();  
  15.     }  
  16.     return 0;  

如果不考虑“内联”(inline),那么编译器在编译代码的时候就已经知道程序中有printHello与printGoodbye这两个函数了,于是会直接生成调用这些函数的指令。而函数地址实际上是硬编码在指令之中的。若是将刚才那段代码写成下面这样,会如何呢?
 

  
  
  1. #import <stdio.h> 
  2.  
  3. void printHello() {  
  4.     printf("Hello, world!\n");  
  5. }  
  6. void printGoodbye() {  
  7.     printf("Goodbye, world!\n");  
  8. }  
  9.  
  10. void doTheThing(int type) {  
  11.     void (*fnc)();  
  12.     if (type == 0) {  
  13.         fnc = printHello;  
  14.     } else {  
  15.         fnc = printGoodbye;  
  16.     }  
  17.     fnc();  
  18.     return 0;  

这时就得使用“动态绑定”(dynamic binding)了,因为所要调用的函数直到运行期才能确定。编译器在这种情况下生成的指令与刚才那个例子不同,在第一个例子中,if与else语句里都有函数调用指令。而在第二个例子中,只有一个函数调用指令,不过待调用的函数地址无法硬编码在指令之中,而是要在运行期读取出来。

在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全于运行期决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为一门真正的动态语言。

给对象发送消息可以这样来写:
 

  
  
  1. id returnValue = [someObject messageName:parameter]; 

在本例中,someObject叫做“接收者”(receiver),messageName叫做“选择子”(selector)。选择子与参数合起来称为“消息”(message)。编译器看到此消息后,将其转换为一条标准的C语言函数调用,所调用的函数乃是消息传递机制中的核心函数,叫做objc_msgSend,其“原型”(prototype)如下:
 

  
  
  1. void objc_msgSend(id self, SEL cmd, ...) 

这是个“参数个数可变的函数”(variadic function),能接受两个或两个以上的参数。第一个参数代表接收者,第二个参数代表选择子(SEL是选择子的类型),后续参数就是消息中的那些参数,其顺序不变。选择子指的就是方法的名字。“选择子”与“方法”这两个词经常交替使用。编译器会把刚才那个例子中的消息转换为如下函数:
 

  
  
  1. id returnValue = objc_msgSend(someObject,  
  2.                               @selector(messageName:),  
  3.                               parameter); 

objc_msgSend函数会依据接收者与选择子的类型来调用适当的方法。为了完成此操作,该方法需要在接收者所属的类中搜寻其“方法列表”(list of methods),如果能找到与选择子名称相符的方法,就跳至其实现代码。若是找不到,那就沿着继承体系继续向上查找,等找到合适的方法之后再跳转。如果最终还是找不到相符的方法,那就执行“消息转发”(message forwarding)操作。消息转发将在第12条中详解。

这么说来,想调用一个方法似乎需要很多步骤。所幸objc_msgSend会将匹配结果缓存在“快速映射表”(fast map)里面,每个类都有这样一块缓存,若是稍后还向该类发送与选择子相同的消息,那么执行起来就很快了。当然啦,这种“快速执行路径”(fast path)还是不如“静态绑定的函数调用操作”(statically bound function call)那样迅速,不过只要把选择子缓存起来了,也就不会慢很多,实际上,消息派发(message dispatch)并非应用程序的瓶颈所在。假如真是个瓶颈的话,那你可以只编写纯C函数,在调用时根据需要,把Objective-C对象的状态传进去。

前面讲的这部分内容只描述了部分消息的调用过程,其他“边界情况”(edge case)则需要交由Objective-C运行环境中的另一些函数来处理:

objc_msgSend_stret。如果待发送的消息要返回结构体,那么可交由此函数处理。只有当CPU的寄存器能够容纳得下消息返回类型时,这个函数才能处理此消息。若是返回值无法容纳于CPU寄存器中(比如说返回的结构体太大了),那么就由另一个函数执行派发。此时,那个函数会通过分配在栈上的某个变量来处理消息所返回的结构体。

objc_msgSend_fpret。如果消息返回的是浮点数,那么可交由此函数处理。在某些架构的CPU中调用函数时,需要对“浮点数寄存器”(floating-point register)做特殊处理,也就是说,通常所用的objc_msgSend在这种情况下并不合适。这个函数是为了处理x86等架构CPU中某些令人稍觉惊讶的奇怪状况。

objc_msgSendSuper。如果要给超类发消息,例如[super message:parameter],那么就交由此函数处理。也有另外两个与objc_msgSend_stret和objc_msgSend_fpret等效的函数,用于处理发给super的相应消息。

刚才曾提到,objc_msgSend等函数一旦找到应该调用的方法实现之后,就会“跳转过去”。之所以能这样做,是因为Objective-C对象的每个方法都可以视为简单的C函数,其原型如下:
 

  
  
  1. <return_type> Class_selector(id self, SEL _cmd, ...) 

真正的函数名和上面写的可能不太一样,笔者用“类”(class)和“选择子”(selector)来命名是想解释其工作原理。每个类里都有一张表格,其中的指针都会指向这种函数,而选择子的名称则是查表时所用的“键”。objc_msgSend等函数正是通过这张表格来寻找应该执行的方法并跳至其实现的。请注意,原型的样子和objc_msgSend函数很像。这不是巧合,而是为了利用“尾调用优化”(tail-call optimization)技术,令“跳至方法实现”这一操作变得更简单些。

如果某函数的最后一项操作是调用另外一个函数,那么就可以运用“尾调用优化”技术。编译器会生成调转至另一函数所需的指令码,而且不会向调用堆栈中推入新的“栈帧”(frame stack)。只有当某函数的最后一个操作仅仅是调用其他函数而不会将其返回值另作他用时,才能执行“尾调用优化”。这项优化对objc_msgSend非常关键,如果不这么做的话,那么每次调用Objective-C方法之前,都需要为调用objc_msgSend函数准备“栈帧”,大家在“栈踪迹”(stack trace)中可以看到这种“栈帧”。此外,若是不优化,还会过早地发生“栈溢出”(stack overflow)现象。

在实际编写Objective-C代码的过程中,大家无须担心这一问题,不过应该了解其底层工作原理。这样的话,你就会明白,在发送消息时,代码究竟是如何执行的,而且也能理解,为何在调试的时候,栈“回溯”(backtrace)信息中总是出现objc_msgSend。

要点

消息由接收者、选择子及参数构成。给某对象“发送消息”(invoke a message)也就相当于在该对象上“调用方法”(call a method)。

发给某对象的全部消息都要由“动态消息派发系统”(dynamic message dispatch system)来处理,该系统会查出对应的方法,并执行其代码。

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值