进击的皮皮虾iOS相关

真正理解Objective-C中的类(二)

2017-03-26  本文已影响293人  柳豪

这篇文章较长,请拿出一些耐心,一定能帮到你

Part 1 Objective-C 中的类

类的定义和简单解释

在面向对象程序设计,类(英语:class)是一种面向对象计算机编程语言的构造,是创建对象的蓝图,描述了所创建的对象共同的属性和方法。

类的更严格的定义是由某种特定的元数据所组成的内聚的包,它描述了一些对象的行为规则,而这些对象就被称为该类的实例。

简单解释一下以上文字描述:

注意第二条,类聚合的一些方法,就是这个类的实例对象所拥有的规则,再简明一点:

对的,类的内部拥有的方法都是实例方法,都是-号开头所定义的方法

你心中一定会有疑惑,因为平时用过类似以下使用类名调用的方法,例如:

...[NSObject alloc]...

以及

NSString * srr = [NSString stringWithFormat:@"%@",someStringObeject];

不要着急,后面会讲到。

一个基本的Objective-C类的表示:

下面继续以前一篇文章中讲到的类为例子看一下OC中一个具体的类

MTTStudent.h

#import <Foundation/Foundation.h>

@interface MTTStudent : NSObject {
    @public
    NSString *_name;
    NSUInteger _age;
    CGFloat _height;
}


@end

(实例变量属于 OC2.0以前的写法,2.0之后大多数使用属性,2.0兼容以前的版本)

解释一下以上代码:
接下来看看他的基本用法:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        MTTStudent * Kangkang = [[MTTStudent alloc] init];
        Kangkang->_age = 25;//实例变量age声明的时候是public,可在外部直接访问的
    }
    return 0;
}

其中Kangkang->_age = 25;这一熟悉的语句,和结构体变量的访问如出一辙

但是,这里你需要细心一点,这是一个缩写缩写缩写实际的代码可以是:

(* &kangkang)->_age = 25;

所以Kangkang这个变量是指向一块某个地址的存放了某种结构数据的的一个指针变量,这种引用类型几乎覆盖类所有OC对象

所以基本上在OC中你使用的每一个对象都是在操作指针

类的实例方法

但是OC对象有一些比结构体多出来的特性,接下来将以上代码替换为下面的代码:

MTTStudent.h

#import <Foundation/Foundation.h>

@interface MTTStudent : NSObject {
    @public
    NSString *_name;
    NSUInteger _age;
    CGFloat _height;
}

- (void)writeArticles;

@end

MTTStudent.m

#import "MTTStudent.h"

@implementation MTTStudent

- (void)writeArticles {
    NSLog(@"feel good,feel high");
}

@end

以上代码,在MTTStudent.h中声明了一个方法- (void)writeArticles;,这是在 C 结构体中所没有的

这个就很熟悉,使用一个该类的实例可以调用实例方法

(但是如果要讲仅仅就这些,我就没有必要BB了)

Part 2 Objective-C 类的继承体系

前面提到过面对对象的三大特性:继承、封装、多态,下面的内容主要和继承有关

  • 继承关系的定义是怎么描述的需要你再回顾一下

  • 绝大部分OC对象的根类都是NSOject类, NSOject类是个什么东西,要好好一起思考一下了

  • 上面提到过Objective-C类的定义中,类的结构体内部只定义了实例方法,那么类方法在哪儿需要搞清楚

继承的特性

如果一个类A“继承自”另一个类B,就把这个A称为“B的子类”,而把B称为“A的父类”也可以称“B是A的超”。

NSOject 的结构和功能

因为所有OC中的类都继承自NSOject类,根据继承的特点,我们只需要搞清楚这个NSOject类是个什么鬼就行了

我们来一起看一下Apple暴露出来的头文件

NSObject.h

@interface NSObject <NSObject> {
    Class isa ;
}
//方法列表省略很多,只保留一个
+ (void)load;
...
...
//方法列表结束

@end

我们再来看看这个Class类是个什么东西,点进去看一下Class的定义:

点进去到了objc.h中:

objc.h

typedef struct objc_class *Class;

前面回顾过typedef 的作用,一起看看这里是个什么鬼,慢慢来,这句代码的含义是:

简洁一点:这句代码就是用typedefstruct objc_class这个结构体重新命名为Class

接下来在看看struct objc_class,这个结构体本身是个什么东西,点进去看一下:

runtime.h

struct objc_class {
    Class isa ;
    Class super_class ;
    //OC2.0之后精简了很多
} 

将上面的NSObject类的内部还原一下:

@interface NSObject <NSObject> {
   struct objc_class * isa ;   
}

@end

初步小结论

特别注意objc_class结构体里面还需要注意另一个变量super_class

super_class指向的就是这个类的父类,super_class就是继承体系构成的关键,它的作用在运行时期某个对象调用方法的时候在某些情况下可以根据它的指向查找到父类中的实例变量和方法

对初步结论的深究

第二点和第三点是一个相关的问题,下面会讲清楚

如何理解指向是其本身类型的ISA指针

其实这个标题其实是很奇怪的,奇怪在哪儿呢?奇怪在于这句话有语意错误

之所以会拿出这个标题,是因为很多人在试图理解中的isa是什么东西的时候看到如下代码的时候发现 struct objc_class结构体的首个成员是Class isa ; ,而同时Class类型本身就是struct objc_class类型 ,于是懵逼了 :口,至少我是懵逼了很久,哈哈

runtime.h

struct objc_class {
    Class isa ;
    Class super_class ;
    ...
    ...
} 

看完下面之后你要是还懵逼的话可以私信联系我 ; )

类对象

也许你已经有过一个模糊的概念:Objective-C中的类也是一种对象?

首先确认一下这个说法的正确性:是对的。

类也是一种对象,可以把它叫做类对象

每个类对象都是程序运行期间的一个单例对象,也就是在程序运行期间只允许唯一的一个存在。
你的Xcode在你创建了重名的类的时候会抛出错误,这属于编译时期诊断出来的错误,IDE只是提前了这一步骤,告诉你每个类只允许存在一个。

类对象 所属的类被称为元类(metaclass),元类这个结构体很特殊,我们平时基本不可能直接使用到它。

当你创建一个类的实例时候,同时会创建与这个类唯一相关的元类,元类的结构和struct objc_class的结构是一致的,只是实例话之后的元类对象的存放地址可能不一样,这就是上面分析struct objc_class 的is a指针居然也是struct objc_class的原因,因为这时候的实例的is a指针指向这个类的结构体,而类的isa指针指向的是存放元类的结构体实例的地址

Part 3 总结

Objective-C中类的本质是对结构体的封装

你操作的所有的实例对象和类对象的数据以及其方法,都是通过OC中的类和对象的指针关系,最终到对应的结构体中去找到相应的数据和方法的,而方法的本质又是对数据的操作罢了,聚合这些数据的方式都是结构体

Objective-C 中的继承关系如下,需要注意的是 is a指针的指向:

文字再叙述:一遍:

真的清楚了吗?试着解释一下以下代码的含义,深入到结构体层面

  NSString * potinterVariable = @"Some String";

期待你的评论,与我一起交流

我的个人微信:lyle92

Objective-C 的对象好像并不是很强大?后面我会讲一讲runtime


参考:维基百科,《Effective Objective-C 2.0》

上一篇 下一篇

猜你喜欢

热点阅读