进击的皮皮虾iOS DeviOS相关

Objective-C Runtime (一)

2017-04-07  本文已影响48人  柳豪
computer-1245714_1920.jpg

讲述背景

平时在网络上和工作中,与各个工种的开发人员进行交流的时候,我发现过一个问题,很多人对面对对象的一些基础概念含糊不清,甚至一无所知。

具体到iOS开发界,常见的开发人员也只是在盲目的堆砌业务功能,而不去考虑这些代码在基础层面的含义。当然基础具体到哪一层次,我只能尽力。

我的观点是,人与人的讨论是需要有某些双方一致认同的基础的,否则,最后也难以得出一个一致认同的结果,自说自话毫无意义。

所以我觉得需要把很多概念弄清楚,不要混用。


讲解runtime所需的前置知识

Objective-C 是一门动态语言

动态语言是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。C、C++等语言则不属于动态语言。Objective-C 的动态特性是由其Runtime环境来实现的

动态性的表现

C++里类别与方法的关系严格清楚,一个方法必定属于一个类别,而且在编译时(compile time)就已经紧密绑定,不可能调用一个不存在类别里的方法。

在Objective-C,类别与消息的关系比较松散,调用方法视为对对象发送消息,所有方法都被视为对消息的回应。

所有消息处理直到运行时(runtime)才会动态决定,并交由类别自行决定如何处理收到的消息。

也就是说,一个类别不保证一定会回应收到的消息,如果类别收到了一个无法处理的消息,程序只会抛出异常,不会出错或崩溃。

对runtime 前置说明

前面的两篇文章讲到了Objective-C类的结构,现在将对结构展开讲述其内部,会首先讲到平时开发中常用的实例、类的常用方法,然后再深入到runtime层面去理解这些方法。

所以,接下来会讲 Objective-C基本代码的常见表达,然后再去深入到runtime层面去理解你平时常写的这些代码。

当然常用的代码的讨论需要建立一些基础的概念,后面会一一提到。


实例变量和属性的关系

实例变量instance variables,也就是常见的ivars,是对类创建的时候用以描述其实例的组成中的数据的封装。是面对对象的基础概念之一。

实例变量的细节

前面的两篇文章已经讲述过,Objective-C的对象,类,类对象,元类都是由结构体实现的

拿C语言进行举例,前面提到过C语言结构体成员的两种访问方式,其实还有另外一种方式,就是得到结构体变量在内存中的首地址,根据每个成员的长度根据偏移量来访问其任何一个成员变量。

事实上这种方式对C语言系的编程语言都是通用的。

但是Objective-C 融和 runtime 使用一些巧妙的办法,使得程序再运行期间去确定偏移量,这就是动态性的表现之一。

再看一看 OC2.0以前 的实例变量的写法

#import <Foundation/Foundation.h>

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

@end

采用这种方式使得MTTStudent的某个实例的内存布局在编译时期就已经固定了,碰到要访问_name变量的代码,编译器就会把访问代码转换会偏移量,表示该变量距离MTTStudent的实例的存储地址有多远,通过操作指针中地址来访问这块存储了_name的内存。

这个偏移量是一个硬编码,编译时候用某种长度的整数记录下来。

可是,如果你要插入了一个新的实例变量

#import <Foundation/Foundation.h>

@interface MTTStudent : NSObject {
    @public
    NSDate *_birthDay;//新增一个实例变量
    NSString *_name;
    NSUInteger _age;
    CGFloat _height;

}

@end

新增并插入的的实例变量_birthDay,如以上代码所示,这个如果继续是访问_name的时候使用原来的偏移量将得到错误的地址,重新编译才能获得新的内存布局,使用心得偏移量

Objective-C的做法是,把实例变量当作 一种存储偏移量的“特殊变量”,由类对象来保存,可以调用runtime的借口获取到具体的偏移量。简单来讲就是在运行时分配内存确定偏移量,并且这个偏移量和实例变量绑定起来,后面讲runtime的接口会看到。

偏移量会在运行期查找,如果类的定义变了,那么存储的偏移量也会变,能保证无论何时访问实例变量,都能使用正确的偏移量。

属性的细节

你需要时刻记住面对对象的特性之一:封装,实例变量是对数据的封装,属性是对实例变量的封装,每一层的封装都隐藏了一些融合的细节在其下一层。

属性最终是采用实例变量来实现的,可以把属性理解为一种简称:编译器会自动写出一套存取方法用以访问类中的实例变量。

属性的特质

所谓属性的特质,是指对属性设置某些修饰,这样编译器在自动实例变量和其存取方法时会考虑这些修饰来生成

属性的特质分为四类:原子性、读写控制、内存管理、方法名

以下的例子是一个具备四类修饰的属性的声明

@property (nonatomic,readwrite,strong,setter=setName:)NSString * name;

关于内存管理语义的细节后面会专门开文章写一写,这些修饰关系到指针的操作,使用不当会导致很多莫名其妙的bug


参考:

题图来自pixabay.com

上一篇 下一篇

猜你喜欢

热点阅读