OC: self
为了更好的说明 Objective-C 中的 self
,我们先从 Java 的 this
关键字开始来引入话题。
Java 中的 this
在 Java 中 this
关键字表示当前类对象,其只能在类的非静态方法中使用,静态方法和静态的代码块中绝对不能出现 this
,this
只和特定的对象关联,而不和类关联,同一个类的不同对象有不同的 this
.
先看一个 Java 示例,能说明上面的问题,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
static { }
public void play() { System.out.println("play()");
this.eat();
this.finish(); }
public static void eat() { System.out.println("static eat()");
}
public void finish() { System.out.println("finish()"); }
|
通过实际的 Java 例子,基本表明了在静态方法和实例方法中 this
的使用场景。
Objective-C 中的 self
在 Objective-C
中,self
是一个比较特殊的对象,它既可以是实例对象也可以是类对象,有点类似于上面 Java 中的 this
关键字。
下面结合实际例子,来说明 self
这个关键字。
1、实例方法中的 self
实例方法中的 self
可以直接调用实例方法但不可以直接调用类方法,如下示例中,调用实例方法 finish
没有问题,而调用类方法 eat
直接报编译错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| - (void)play { NSLog(@"---------------- '- (void)play' ------------------"); NSLog(@"self: %@, self -> %p", self, self); [self finish]; }
+ (void)eat { NSLog(@"---------------- '+ (void)eat' ------------------"); }
- (void)finish { NSLog(@"--------------- '- (void)finish' ----------------"); }
|
我们知道,在实例方法中可以直接通过``[类 类方法]的方式来调用类方法,那么如果想在实例方法中使用
self` 关键字,如何办呢?
很简单,使用 [self class]
即可。
1 2 3 4 5 6 7
| - (void)play { NSLog(@"---------------- '- (void)play' ------------------"); NSLog(@"self: %@, self -> %p", self, self); [[self class] eat]; }
|
关于 class
后续再分享给大家,这里只需要知道可以这么使用就好了。
2、类方法中的 self
这个跟 Java 的 this
有点不一样,上面的 Java 示例中我们可以看到无论是打印 this
还是使用 this
调用方法都不可以,但是在 Objective-C
中却可以使用 self
,只是不能使用 self
来调用实例方法和实例变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| + (void)eat { NSLog(@"---------------- '+ (void)eat' ------------------"); [self beat]; NSLog(@"self: %@", self); }
+ (void)beat { NSLog(@"---------------- '+ (void)beat' ------------------"); }
|
那么为什么在类方法中可以使用 self
呢?
别着急,接着往下看。
3、实例和类方法中的 self 区别
其实,在类方法中,self
表示当前类对象,在实例方法中 self
表示实例对象,这个是本质区别,务必要理解透彻。
举个例子,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| - (void)play { NSLog(@"---------------- '- (void)play' ------------------"); NSLog(@"self: %@, self -> %p", self, self); NSLog(@"self class: %p", [self class]); [[self class] eat]; }
+ (void)eat { NSLog(@"---------------- '+ (void)eat' ------------------"); NSLog(@"self: %p", self); [self beat]; }
+ (void)beat { NSLog(@"---------------- '+ (void)beat' ------------------"); NSLog(@"self: %p", self); }
|
在实例方法 play
中打印类地址,在类方法 eat
和 beat
中打印 self
的地址,输出结果是一样的,都是 0x10adb3f98
这个地址。
1 2 3 4 5 6 7
| ---------------- '- (void)play' ------------------ self: <MZPerson: 0x6000000d8f90>, self -> 0x6000000d8f90 self class: 0x10adb3f98 ---------------- '+ (void)eat' ------------------ self: 0x10adb3f98 ---------------- '+ (void)beat' ------------------ self: 0x10adb3f98
|
为了更好的说明,我给大家再举一个形象的例子帮助大家理解。
在 MZPerson
中声明两个方法,方法同名,一个是实例方法,另一个是类方法,如下:
1 2 3 4 5 6 7 8 9 10 11
| @interface MZPerson : NSObject
- (void)play;
+ (void)play;
+ (void)eat;
- (void)finish;
@end
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @implementation MZPerson
- (void)play { NSLog(@"---------------- '- (void)play' ------------------"); }
+ (void)play { NSLog(@"---------------- '+ (void)play' ------------------"); }
+ (void)eat { NSLog(@"---------------- '+ (void)eat' ------------------"); [self play]; }
- (void)finish { NSLog(@"---------------- '- (void)finish' ------------------"); [self play]; }
@end
|
在类方法 eat
中调用 [self play]
在实例方法 finish
中也调用 [self play]
,那么结果如何呢?
1 2 3 4 5
| ---------------- '- (void)finish' --------------- ---------------- '- (void)play' -----------------
---------------- '+ (void)eat' ------------------ ---------------- '+ (void)play' -----------------
|
可以看出符合如期,类和实例方法中的 self 分别代表类本身和实例对象。
self
表示谁,在运行时是由编译器来决定的。
4、每个实例对象的 self 都是不一样的
这个跟 Java 的 this
是一样的,每个类的实例对象对应的 this
都是不一样的,self
亦如此。
下面的例子,分别创建两个 MZPerson 实例对象,然后分别调用play
方法,如下:
1 2 3 4 5
| MZPerson *iperson1 = [MZPerson new]; [iperson1 play]; MZPerson *iperson2 = [MZPerson new]; [iperson2 play];
|
输出结果表明了上面说法的正确性。
1 2 3 4
| ---------------- '- (void)play' ------------------ self: <MZPerson: 0x600000576ee0>, self -> 0x600000576ee0 ---------------- '- (void)play' ------------------ self: <MZPerson: 0x600000576f40>, self -> 0x600000576f40
|
最后
在继承关系中,使用 self
调用方法时,首先从当前类的方法列表中开始寻找,如果没有再从父类中寻找。
运行时(runtime)会使用 objc_msgSend
向对象发送消息,这个也是调用方法的本质。
扫码关注,你我就各多一个朋友~