NSObject之二

前面一章我们整理了NSObject类,这一章我们来看看NSObject协议的内容。

NSObject协议提供了一组方法作为Objective-C对象的基础。其实我们对照一个NSObject类和NSObject协议,可以看到很多方法的方法名都是一样的,只不过NSObject类提供的是类方法,是基于类级别的操作;而NSObject协议提供的是实例方法,是基于实例对象级别的操作。

如果一个对象如果采用了这个协议,则可以被看作是一级对象。我们可以从这个对象获取以下信息:

  1. 类信息,以及类所在的继承体系。
  2. 协议信息
  3. 响应特定消息的能力

实际上,Cocoa的根类NSObject就采用了这个类,所以所有继承自NSObject类的对象都具备NSObject协议中描述的功能。接下来,我们参照NSObject类,整理一下这些功能。

识别对象

类似于NSObject类,NSObject协议提供了一些方法来识别类。

如果想获取对象的类对象,则可以使用如下方法:

- (Class)class

如果想获取对象父类的类对象,则可以使用以下只读属性:

@property(readonly) Class superclass

如果想查看某个对象是否是给定类的实例或者是给定类子类的实例,则可以使用以下方法:

- (BOOL)isKindOfClass:(Class)aClass

这个方法应该是大家常用的方法。需要注意的是在类簇中使用这个方法。在类簇中,我们获取到的对象类型可能并不是我们期望的类型。如果我们调用一个返回类簇的方法,则这个方法返回的实际类型会是最能标识这个类能做些什么的类型。例如,如果一个方法返回一个指向NSArray对象的指针,则不能使用isKindOfClass:方法查看经是否是一个可变数组,如以下代码:

if ([myArray isKindOfClass:[NSMutableArray class]])
{
    // Modify the object
}

如果我们使用这样的代码,我们可能会认为修改一个实际上不应该被修改的对象是没问题的。这样做可能会对那些期望对象保持不要变的代码产生影响。

另外,查看对象是否是指定类的一个实例还可以使用以下方法:

- (BOOL)isMemberOfClass:(Class)aClass

注意,这个方法无法确定对象是否是指定类子类的实例。另外,类对象可能是编译器创建的对象,但它仍然支持这一概念。

测试对象

对于对象的测试,NSObject协议也定义了两个方法,其中respondsToSelector:方法用于测试对象是否能响应指定的消息,这个方法可以是类自定义的实例方法,也可以是继承而来的实例方法。其声明如下:

- (BOOL)respondsToSelector:(SEL)aSelector

不过我们不能使用super关键字来调用respondsToSelector:,以查看对象是否是从其父类继承了某个方法。因为我们可以从super的定义可知,消息的最终实际接收者还是self本身,因此测试的还是对象的整个体系(包括对象所在类本身),而不仅仅是父类。不过,我们可以使用父类来调用NSObject类的类方法instancesRespondToSelector:来达到这个目的,如下所示:

if( [MySuperclass instancesRespondToSelector:@selector(aMethod)] ) {
    // invoke the inherited method
    [super aMethod];
}

我们不能简单地使用[[self superclass] instancesRespondToSelector:@selector(aMethod)],因为如果由一个子类来调用,则可能导致方法的失败。

还需要注意的是,如果对象能够转发消息,则也可以响应这个消息,不过这个方法会返回NO。

如果想查看对象是否实现了某个类,则可以使用如下方法:

- (BOOL)conformsToProtocol:(Protocol *)aProtocol

这个方法与NSObject类的类方法conformsToProtocol:是一样的。它只是提供了一个便捷方法,我们不需要先去取对象的类,再调用类方法conformsToProtocol:。

标识和比较对象

如果我们想获取对象本身,则可以使用以下方法:

- (instancetype)self

比较两个对象是否相同,则可以使用以下方法:

- (BOOL)isEqual:(id)anObject

这个方法定义了对象相同的意义。例如,一个容器对象可能会按照特定规则来定义两个对象是否相等,如其所有元素的isEqual:请求都返回YES。我们在自定义子类时,可以重写这个方法,以使用我们自己的规则来评判两个对象相等。

如果两个对象相等,则它们必须拥有相同的hash值。在子类中定义isEqual:方法并打算把子类的实例放入集合中时,这一点非常重要。因此在子类中必须同时定义hash。

hash值是一个整数值,它可以用于在hash表结构中作为一个表地址。其声明如下:

@property(readonly) NSUInteger hash

如果一个可变对象被添加到一个以hash值来确定对象位置的集合中,则当对象还在集合中时,其由hash方法返回的值不能改变。因此,hash方法不能依赖于对象内部的任何状态信息,或许我们必须确保对象在集合中时,不能改变其内部状态信息。比如,一个可变字典可以放到一个hash表中,但当它还在表中时,不能改变它。

发送消息

在NSObject类中,定义了一系列的发送消息的方法,用于在目标线程中执行方法。NSObject协议也定义了如下几个方法,来执行发送消息的任务:

- (id)performSelector:(SEL)aSelector

- (id)performSelector:(SEL)aSelector withObject:(id)anObject

- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject

这三个方法基本相同,只不过后面两个方法能为selector指定的方法携带参数。因此我们以performSelector:为例。

performSelector:方法的使用与直接将消息发送给对象的效果是一样的,如下面几个操作,做的事情是一样的:

id myClone = [anObject copy];
id myClone = [anObject performSelector:@selector(copy)];
id myClone = [anObject performSelector:sel_getUid("copy")];

区别在于,performSelector:允许在运行时再去确定对象是否能处理消息。而[anObject copy]中,如果anObject不能处理copy,编译器就直接会报错。

如果方法的参数过多,以至于上面几个方法都无法处理,则可以考虑使用NSInvocation对象。

描述对象

描述对象的方法与NSObject类中描述类的方法其方法名相同,都是description,其声明如下:

@property(readonly, copy) NSString *description

这个方法用于创建一个对象的文本表达方式,例如:

ClassName *anObject = <#An object#>;
NSString *string = [NSString stringWithFormat:@"anObject is %@", anObject];

为了便于调试,NSObject协议还定义debugDescription方法,该方法声明如下:

@property(readonly, copy) NSString *debugDescription

该方法返回一个在调试器中显示的用于描述对象内容的字符串。在调试器中打印一个对象时,会调用这个方法。NSObject类实现这个方法时只是调用了description方法,所以默认情况下,这两个方法的输出都是一样的。我们在子类中可以重写这个方法的实现。

总结

NSObject协议的定义的很多方法都是我们平常经常使用的。我们在创建NSObject类的子类时,默认都继承了NSObject类对于NSObject协议的实现。如果有特殊的需求,我们可以重写这些方法。

当然,NSObject协议还定义了一些方法,如我们非常熟悉的retain, release, autorelease, retainCount方法,不过这些方法在ARC时代已经过时了,我们在此不过多说明。

参考

  1. NSObject Protocol Reference

http://southpeak.github.io/blog/2015/01/31/nsobjectzhi-er/

时间: 2024-12-28 20:46:32

NSObject之二的相关文章

黑马程序员— block、protocol和Foundation

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 上一章我们学习了OC内存管理的相关知识,了解了OC程序在运行中对象及变量是如何释放以达到不占用内存的效果.本章我们来学习OC的block,protocol协议和Foundation框架. 第一讲     block 1. block简介 block封装了一段代码,可以在任何时候执行 block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值. 苹果官方建议尽量多用block.

iOS编码规范(简版)

1. 总体指导原则 [规则1-1]首先是为人编写程序,其次才是计算机. 说明:这是软件开发的基本要点,软件的生命周期贯穿产品的开发.测试.生产.用户使用.版本升级和后期维护等长期过程,只有易读.易维护的软件代码才具有生命力,所以提倡写代码之前多思考,特别是逻辑复杂或者技术难点较高的地方,个人思考不清楚的,可以和团队成员进行沟通. [规则1-2]保持代码的简明清晰,避免过分的编程技巧. 说明:简单是最美.保持代码的简单化是软件工程化的基本要求.不要过分追求技巧,否则会降低程序的可读性. [规则1-

OC 中的重写 OC中没有重载 以及隐藏

一.定义: 重载.重写和隐藏是很容易混淆的类似概念.虽然所有这三种技术都使您得以创建同名的成员,但它们之间有一些重要的差异. 重载的成员用于提供属性或方法的不同版本,这些版本具有相同名称但是接受不同数量的参数或者接受不同数据类型的参数. 重写的属性和方法用于替换在派生类中不适合的继承的属性或方法.重写的成员必须接受同一数据类型和参数数量.派生类继承重写的成员. 隐藏的成员用于局部替换具有更广范围的成员.任何类型都可隐藏任何其他类型.例如,可声明隐藏同名继承方法的属性.无法继承隐藏的成员. Obj

OC基础(五)面向对象的三大特性:封装、继承、多态

一.封装: 封装是屏蔽内部实现的细节, 仅仅对外提供共有的方法/接口 好处: 保证数据的安全性,将变化隔离 规范: 一般情况下不会对外直接暴露成员变量, 都会提供一些共有的方法进行赋值成员变量都需要封装起来 注:1.若一个类把自己的成员变量暴露给外部的时候,那么该类就失去对该成员变量的管理权,别人可以任意的修改你的成员变量. 2.封装中经常使用@property(即setter和getter方法) 3.在类的延展中对类进行分装,即: @interface NSObject () @end 二.继

YYModel 源码解读(二)之NSObject+YYModel.h (1)

本篇文章主要介绍 _YYModelPropertyMeta 前边的内容 首先先解释一下前边的辅助函数和枚举变量,在写一个功能的时候,这些辅助的东西可能不是一开始就能想出来的,应该是在后续的编码过程中 逐步添加的. #define force_inline __inline__ __attribute__((always_inline)) 这行代码用到了C语言的内联函数 内联函数: 是用inline修饰的函数,内联函数在代码层次看和普通的函数结构一样,却不具备函数的性质,内联函数不是在调用时发生控

NSObject头文件解析 / 消息机制 / Runtime解读 (二)

本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount) 其中有一个参数我们再在上一篇中提到过 typedef struct { const char *name;           /**< The na

YYModel 源码解读(二)之NSObject+YYModel.h (4)

接下来我们继续向下看 typedef struct { void *modelMeta; ///< _YYModelMeta void *model; ///< id (self) void *dictionary; ///< NSDictionary (json) } ModelSetContext; 这是一个c的结构体,在c中 void * 相当于 oc 中的 id 类型 那么 为什么使用c的结构体呢,最主要的使用场景就是我们需要同时使用多个参数的情况下,可以使用c的结构体 /**

YYModel 源码解读(二)之NSObject+YYModel.h (5)

好了,之前的博文中详细的解释了一些辅助的类和辅助的函数,接下来就是使用它们来实现酷炫功能的时候,正所谓磨刀不误砍柴工啊 我们先把总的功能罗列出来 1. json转字典              + (NSDictionary *)_yy_dictionaryWithJSON:(id)json 2. json转模型              + (instancetype)yy_modelWithJSON:(id)json 3. 字典转模型              + (instancetype

YYModel 源码解读(二)之NSObject+YYModel.h (3)

本篇主要介绍的是 在真正转之前的几个辅助函数 /** Get number from property. @discussion Caller should hold strong reference to the parameters before this function returns. @param model Should not be nil. @param meta Should not be nil, meta.isCNumber should be YES, meta.get