在许多地方可以见到self和[self class]的调用方式,那么他们有区别吗?
1 首先self是什么,它是指向实例变量首地址的指针(同c++的this一样)可以访问对象的资源。
2 [self class] 首先看下class 它返回的是类对象(也就是isa指针),所以通过[self class]可以访问当前类的静态函数
这样就引出两个问题 :A、[self class]为什么返回的是类对象 B、为什么[self class]可以访问当前类的类方法
A、为什么[self class] 可以访问到类指针呢,可以看下NSObject的结构:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
//任何类都是继承于NSObject所以在内存中isa是首地址 ,self指向的就是isa的的地址。
看下在runtime里面class是怎么调用的,首先是获取NSObject 类的isa变量,通过isa来返回类对象,所以也就是说class是返回了当前类。
- (Class)class {
return object_getClass(self);
}
Class object_getClass(id obj)
{
if (obj) return obj->getIsa();
else return Nil;
}
B、为什么[self class]可以访问当前类的类方法
首先先来学习下实例对象是怎么调用实例方法的,介绍运行时的知识中提到:对象对方法的调用是通过isa间接去调用,
也就是说 [self Test1] 首先访问当前对象的isa,通过这个类对象在访问函数列表,从而调用方法Test1,概况起来就是:消息发送给对象,对象转交给其isa指向类去处理(类方法的调用也一样)
@interface Sark : NSObject
+(void)Test;
@end
类方法的调用顺序是 :[self class]返回类对象(isa指针)指向这个类如:Sark类,这个类里面又包含一个指向元类的类对象,当调用 [[self class] Test]跟对象调用一样,会通过当前类 访问其元类继而
访问类方法。
总结:无论是对象,还是类,都是通过访问其isa来调用函数 ,区别是调用的是实例方法还是类方法。
我们先来看一个例子:
+(void)Test{
NSLog(@"static test");
}
-(void)Test1{
NSLog(@"tst1");
}
- (void)viewDidLoad {
[super viewDidLoad];
Class c1 = object_getClass(self);
Class c2 = [self class];
Class c3 = object_getClass(c1);
NSLog(@"c1:%p, c2:%p", c1, c2);
[c1 Test];
[c2 Test];
[c3 Test1];
IMP imp1 = class_getMethodImplementation(c1, @selector(Test1));
imp1();
IMP imp2 = class_getMethodImplementation(c2, @selector(Test1));
imp2();
//[[self class]Test];
IMP imp3 = class_getMethodImplementation(c3, @selector(Test));
imp3();
}
Class c1 = object_getClass(self); 和 Class c2 = [self class]; 都是返回当前类(等价关系)
[c1 Test];和[c2 Test]; 在通过类的isa指针访问类方法
3 那么[self class]除了可以访问静态函数,变量还有其他用处吗?看下面这个例子
@interface Engine:NSObject<NSCopying>
@end
@implementation Engine
-(id)copyWithZone:(NSZone *)zone{
return [[[self class]allocWithZone:zone]init];
}
我们需要将消息发送给一个类,而不是一个实例变量。那么应该发送给哪个类呢?直觉告诉我们应该是Engine,像这样:[Engine allocWithZone:zone];
但是这行代码只适用于Engine类,不适合它的之类。如果给它的子类Slant6发送copy消息,最后使用的是Engine类的拷贝而不是slant6类的拷贝,创建出来的时Engine对象。
所以要使用[self class]他可以将消息发送给正在接受copy消息的对象所属的类。如果self是一个Slvant6的对象,就会发送给Slvant6。