在实现Objective-C类的方法时, 我们可以在方法内部用self关键字来引用当前对象或者当前类, 使对象或类自己作为消息的接收者.
用代码来说明, 定义一个类ClassA:
@interface ClassA : NSObject
- (void)instanceMethodA;
- (void)instanceMethodB;
+ (void)classMethodA;
+ (void)classMethodB;
@end
我们来实现它的方法:
- (void)instanceMethodA {
}
- (void)instanceMethodB {
// 调用实例方法, 此时self代表"当前对象"或"当前实例"
[self instanceMethodA];
}
+ (void)classMethodA {
}
+ (void)classMethodB {
// 调用类方法, 此时self代表"当前类"
[self classMethodA];
// 效果等同于[ClassA classMethodA];
}
可以看到, 在实现instanceMethodB时, 给self发送了instanceMethodA消息, 此时, self就引用着”当前对象”, 因为instanceMethodA是个对象方法, 它的接收者必须是一个对象.
同理, 在classMethodB中, 给self发送了classMethodA消息, 由于classMethodA是个类方法, 其接收者必须是一个类, 所以此时self就引用着”当前类”, 即ClassA.
总结起来就是一句话: self引用着当前消息的接收者.
延伸:
我们经常会给一个类设计一个或多个构造方法, 来快速地创建对象. 在构造方法内部, 我们也应该尽量使用self关键字, 而不是直接使用类名, 这是为了构造方法在子类中也能返回正确的子类对象, 而不是父类.
用代码来说明, 在ClassA中定义一个构造方法:
@interface ClassA : NSObject
+ (instancetype)anObject; // 构造方法
@end
实现构造方法, 先看第一种方式(不推荐):
+ (instancetype)anObject {
// 直接使用类名调用alloc
return [[ClassA alloc] init];
}
创建一个ClassB, 继承自ClassA. 看如下的测试代码:
id obj1 = [ClassA anObject];
NSLog(@"%@", NSStringFromClass([obj1 class]));
// 输出ClassA
id obj2 = [ClassB anObject];
NSLog(@"%@", NSStringFromClass([obj2 class]));
// 输出ClassA
我们用ClassB调用anObject方法, 返回的对象依然是ClassA类型, 这显然就达不到我们本来的目的了.
再看第二种实现方式(推荐写法):
+ (instancetype)anObject {
// 使用self调用alloc
return [[self alloc] init];
}
此时, 再次运行:
id obj2 = [ClassB anObject];
NSLog(@"%@", NSStringFromClass([obj2 class]));
// 输出ClassB
这就达到我们预期的效果了.
另外, 在很多地方还能看到第三种实现方式, 用[self class]代替self:
+ (instancetype)anObject {
return [[[self class] alloc] init];
}
这其实和第二种方式是等效的, 因为无论是self或者[self class], 此时代表的东西是一样的, 都是”当前类”.
最后建议, 在设计初始化方法或者构造方法时, 应该用instancetype作为返回类型, 这有助于类型安全, 详情可参阅我的这篇博文.