实例方法和类方法的调用

在许多地方可以见到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。

原文地址:https://www.cnblogs.com/HypeCheng/p/4629680.html