快捷搜索:  MTU2MjE0MTg5NQ`

iOS-NSObject官方API大总结

先看下NSObject承袭图.

NSObject承袭图.png

阐明:

原先想翻译一下 然则英语水平有限,怕翻译的会误导大年夜家,终极直接原文展示

本篇包括2部分,前面是官方文档,后面是一大年夜神"南峰子"的博客内容,小我感到写的很好,看完之后感到劳绩很多.

涉猎本文的话,必要懂得runtime,否则有些地方可能理解起来费劲点,但绝对值得收藏.

一 : 基础观点

NSObject 是 大年夜部分常见OC类的根类.恰是因为承袭关系的存在,objects承袭了一些基础特点和能力,去适应运行时系统,并且体现出工具的特性.

二 :Initializing a Class

+ initialize

+ load

三 :Creating, Copying, and Deallocating Objects

+ alloc

+ allocWithZone:

- init //Designated Initializer

- copy

+ copyWithZone:

- mutableCopy

+ mutableCopyWithZone:

- dealloc

+ new

四 :Identifying Classes

+ class

+ superclass

+ isSubclassOfClass:

五 :Testing Class Functionality

+ instancesRespondToSelector:

六 :Testing Protocol Conformance

+ conformsToProtocol:

七 :Obtaining Information About Methods

- methodForSelector:

+ instanceMethodForSelector:

+ instanceMethodSignatureForSelector:

- methodSignatureForSelector:

八 :Describing Objects

+ description

九 :Discardable Content Proxy Support

autoContentAccessingProxy

十 :Sending Messages

- performSelector:withObject:afterDelay:

- performSelector:withObject:afterDelay:inModes:

- performSelectorOnMainThread:withObject:waitUntilDone:

- performSelectorOnMainThread:withObject:waitUntilDone:modes:

- performSelector:onThread:withObject:waitUntilDone:

- performSelector:onThread:withObject:waitUntilDone:modes:

- performSelectorInBackground:withObject:

+ cancelPreviousPerformRequestsWithTarget:

+ cancelPreviousPerformRequestsWithTarget:selector:object:

十一 :Forwarding Messages

- forwardingTargetForSelector:

- forwardInvocation:

十二 :Dynamically Resolving Methods

+ resolveClassMethod:

+ resolveInstanceMethod:

十三 :Error Handling

- doesNotRecognizeSelector:

十四 :Archiving

- awakeAfterUsingCoder:

classForCoder//Property

classForKeyedArchiver//Property

+ classFallbacksForKeyedArchiver

+ classForKeyedUnarchiver

- replacementObjectForCoder:

- replacementObjectForKeyedArchiver:

+ setVersion:

+ version

1

Objective-C中有两个NSObject,一个是NSObject类,另一个是NSObject协议。而此中NSObject类采纳了NSObject协议。在本文中,我们主要收拾一下NSObject类的应用。

说到NSObject类,写Objective-C的人都应该知道它。它是大年夜部分Objective-C类承袭体系的根类。这个类供给了一些通用的措施,工具经由过程承袭NSObject,可以从此中承袭造访运行时的接口,并让工具具备Objective-C工具的基础能力。以下我们就来看看NSObejct供给给我们的一些根基功能。

+load与+initialize

这两个措施可能日常平凡用得对照少,但很有用。在我们的法度榜样编译后,类相关的数据布局会保留在目标文件中,在法度榜样运行后会被解析和应用,此时类的信息会经历加载和初始化两个历程。在这两个历程中,会分手调用类的load措施和initialize措施,在这两个措施中,我们可以适当地做一些定制处置惩罚。欠妥是类本身,类的分类也会经历这两个历程。对付一个类,我们可以在类的定义中重写这两个措施,也可以在分类中重写它们,或者同时重写。

load措施

对付load措施,当Objective-C运行时加载类或分类时,会调用这个措施;平日假如我们有一些类级其余操作必要在加载类时处置惩罚,就可以放在这里面,如为一个类履行Swizzling Method操作。

load消息会被发送到动态加载和静态链接的类和分类里面。不过,只有当我们在类或分类里面实现这个措施时,类/分类才会去调用这个措施。

在类承袭体系中,load措施的调用顺序如下:

一个类的load措施会在其所有父类的load措施之后调用

分类的load措施会在对应类的load措施之后调用

在load的实现中,假如应用同一库中的别的一个类,则可能是不安然的,由于可能存在的环境是别的一个类的load措施还没有运行,即另一个类可能尚未被加载。

别的,在load措施里面,我们不必要显示地去调用[super load],由于父类的load措施会自动被调用,且在子类之前。

在有依附关系的两个库中,被依附的库中的类其load措施会优先调用。但在库内部,各个类的load措施的调用顺序是不确定的。

initialize措施

当我们在法度榜样中向类或其任何子类发送第一条消息前,runtime会向该类发送initialize消息。

runtime会以线程安然的要领来向类提议initialize消息。

父类会在子类之前收到这条消息。

父类的initialize实现可能鄙人面两种环境下被调用:

子类没有实现initialize措施,runtime将会调用承袭而来的实现

子类的实现中显示的调用了[super initialize]

假如我们不想让某个类中的initialize被调用多次,则可以像如下处置惩罚:

+ (void)initialize {

if (self == [ClassName self]) {

// ... do the initialization ...

}

}

留意:

由于initialize因此线程安然的要领调用的,且在不合的类中initialize被调用的顺序是不确定的,以是在initialize措施中,我们应该做少量的必须的事情。

分外必要留意是,假如我们initialize措施中的代码应用了锁,则可能会导致逝世锁。是以,我们不应该在initialize措施中实现繁杂的初始化事情,而应该在类的初始化措施(如-init)中来初始化。

别的,每个类的initialize只会被调用一次。以是,假如我们想要为类和类的分类实现零丁的初始化操作,则应该实现load措施。

假如想具体地懂得这两个措施的应用,可以查看《Effective Objective-C 2.0》的第51条,里面有异常具体的阐明。假如想更深入地懂得这两个措施的调用,则可以参考objc库的源码,别的,NSObject的load和initialize措施一文从源码层面为我们简单先容了这两个措施。

工具的生命周期

一说到工具的创建,我们会急速想到[[NSObject alloc] init]这种经典的两段式构造。对付这种两段式构造,唐巧大年夜神在他的”谈ObjC工具的两段构造模式“一文中作了具体描述,大年夜家可以参考一下。

本小节我们主要先容一下与工具生命周期相关的一些措施。

工具分配

NSObject供给的工具分配的措施有alloc和allocWithZone:,它们都是类措施。这两个措施认真创建工具并为其分配内存空间,返回一个新的工具实例。新的工具的isa实例变量应用一个数据布局来初始化,这个数据布局描述了工具的信息;创建完成后,工具的其它实例变量被初始化为0。

alloc措施的定义如下:

+ (instancetype)alloc

而allocWithZone:措施的存在是由历史缘故原由造成的,它的调用基础上和alloc是一样的。既然是历史缘故原由,我们就不说了,官方文档只给了一句话:

This method exists for historical reasons; memory zones are no longer used by Objective-C.

我们只必要知道alloc措施的实现调用了allocWithZone:措施。

工具初始化

我们一样平常不去自己重写alloc或allocWithZone:措施,不用去关心工具是若何创建、若作甚其分配内存空间的;我们更关心的是若何去初始化这个工具。上面提到了,工具创建后,isa以外的实例变量都默认初始化为0。平日,我们盼望将这些实例变量初始化为我们期望的值,这便是init措施的事情了。

NSObject类默认供给了一个init措施,其定义如下:

- (instancetype)init

正常环境下,它会初始化工具,假如因为某些缘故原由无法完成工具的创建,则会返回nil。

留意:

工具在应用之前必须被初始化,否则无法应用。不过,NSObject中定义的init措施不做任何初始化操作,只是简单地返回self。

当然,我们定义自己的类时,可以供给自定义的初始化措施,以满意我们自己的初始化需求。必要留意的便是子类的初始化措施必要去调用父类的响应的初始化措施,以包管初始化的精确性。

讲完两段式构造的两个部分,有需要来讲讲NSObject类的new措施了。

new措施实际上是集alloc和init于一身,它创建了工具并初始化了工具。它的实现如下:

+ (instancetype)new {

return [[self alloc] init];

}

new措施更多的是一个历史遗留产物,它源于NeXT期间。假如我们的初始化操作只是调用[[self alloc] init]时,就可以直接用new来代替。不过假如我们必要应用自定义的初始化措施时,平日就应用两段式构造要领。

拷贝

说到拷贝,信托大年夜家都很认识。拷贝可以分为“深拷贝”和“浅拷贝”。深拷贝拷贝的是工具的值,两个工具互相不影响,而浅拷贝拷贝的是工具的引用,改动一个工具时会影响到另一个工具。

在Objective-C中,假如一个类想要支持拷贝操作,则必要实现NSCopying协议,并实现copyWithZone:【留意:NSObject类本身并没有实现这个协议】。假如一个类不是直接承袭自NSObject,则在实现copyWithZone:措施时必要调用父类的实现。

虽然NSObject自身没有实现拷贝协议,不过它供给了两个拷贝措施,如下:

- (id)copy

这个是拷贝操作的便捷措施。它的返回值是NSCopying协议的copyWithZone:措施的返回值。假如我们的类没有实现这个措施,则会抛出一个非常。

与copy对应的还有一个措施,即:

- (id)mutableCopy

从字面意义来讲,copy可以理解为弗成变拷贝操作,而mutableCopy可以理解为可变操作。这便引出了拷贝的另一个特点,即可变性。

顾名思义,弗成变拷贝即拷贝后的工具具有弗成变属性,可变拷贝后的工具具有可变属性。这对付数组、字典、字符串、URL这种分可变和弗成变的工具来说是很故意义的。我们来看如下示例:

NSMutableArray *mutableArray = [NSMutableArray array];

NSMutableArray *array = [mutableArray copy];

[array addObject:@"test1"];

实际上,这段代码是会崩溃的,我们来看看崩溃日志:

-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100107070

奸淫 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI addObject:]: unrecognized selector sent to instance 0x100107070'

从中可以看出,颠末copy操作,我们的array实际上已经变成弗成变的了,其底层元类是__NSArrayI。这个类是不支持addObject:措施的。

有时在代码中,也会看到类似于下面的环境:

@property (copy) NSMutableArray *array;

这种属性的声明要领是有问题的,即上面提到的可变性问题。应用self.array = **赋值后,数组着实是弗成变的,以是必要分外留意。

mutableCopy的应用也挺故意思的,详细的还请大年夜家自己去试验一下。

开释

当一个工具的引用计数为0时,系统就会将这个工具开释。此时runtime会自动调用工具的dealloc措施。在ARC情况下,我们不再必要在此措施中去调用[super dealloc]了。我们重写这个措檀越如果为了开释工具顶用到的一些资本,如我们经由过程C措施分配的内存空间。dealloc措施的定义如下:

- (void)dealloc

必要留意的是,我们不应该直接去调用这个措施。这些事都让runtime去做吧。

消息发送

Objective-C中对措施的调用并不是像C++里面那样直接调用,而是经由过程消息分发机制来实现的。这个机制核心的措施是objc_msgSend函数。消息机制的详细实现我们在此不做评论争论,可以参考Objective-C Runtime 运行时之三:措施与消息。

对付消息的发送,除了应用[obj method]这种机制之外,NSObject类还供给了一系列的performSelector措施。这些措施可以让我们加倍机动地节制措施的调用。接下来我们就来看看这些措施的应用。

在线程中调用措施

假如我们想在当火线程中调用一个措施,则可以应用以下两个措施:

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes

这两个措施会在当火线程的Run loop中设置一个准时器,以在delay指定的光阴之后履行aSelector。假如我们盼望准时器运行在默认模式(NSDefaultRunLoopMode)下,可以应用前一个措施;假如想自己指定Run loop模式,则可以应用后一个措施。

当准时器启动时,线程会从Run loop的行列步队中获取到消息,并履行响应的selector。假如Run loop运行在指定的模式下,则措施会成功调用;否则,准时器会处于等待状态,直到Run loop运行在指定模式下。

必要留意的是,调用这些措施时,Run loop会保留措施接管者及相关的参数的引用(即对这些工具做retain操作),这样在履行时才不至于损掉这些工具。当措施调用完成后,Run loop会调用这些工具的release措施,削减工具的引用计数。

假如我们想在主线程上履行某个工具的措施,则可以应用以下两个措施:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array

我们都知道,iOS中所有的UI操作都必要在主线程中处置惩罚。假如想在某个二级线程的操作完成之后做UI操作,就可以应用这两个措施。

这两个措施会将消息放到主线程Run loop的行列步队中,前一个措施应用的是NSRunLoopCommonModes运行时模式;假如想自己指定运行模式,则应用后一个措施。措施的履行与之前的两个performSelector措施是类似的。当在一个线程中多次调用这个措施将不合的消息放入行列步队时,消息的分发顺序与入队顺序是同等的。

措施中的wait参数指定当火线程在指定的selector在主线程履行完成之后,是否被壅闭住。假如设置为YES,则当火线程被壅闭。假如当火线程是主线程,而该参数也被设置为YES,则消息会被急速发送并处置惩罚。

别的,这两个措施分发的消息不能被取消。

假如我们想在指定的线程平分发某个消息,则可以应用以下两个措施:

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thread withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array

这两个措施基础上与在主线程的措施差不多。在此就不再评论争论。

假如想在后台线程中调用接管者的措施,可以应用以下措施:

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

这个措施会在法度榜样中创建一个新的线程。由aSelector表示的措施必须像法度榜样中的其它新线程一样去设置它的线程情况。

当然,我们常常看到的performSelector系列措施中还有几个措施,即:

- (id)performSelector:(SEL)aSelector

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

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

不过这几个措施是在NSObject协议中定义的,NSObject类实现了这个协议,也就定义了响应的实现。这个我们将在NSObject协议中来先容。

取消措施调用哀求

对付应用performSelector:withObject:afterDelay:措施(仅限于此措施)注册的履行哀求,在调用发生前,我们可以应用以下两个措施来取消:

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget

+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument

前一个措施会取消以是接管者为aTarget的履行哀求,不过仅限于当前run loop,而不是所有的。

后一个措施则会取消由aTarget、aSelector和anArgument三个参数指定的履行哀求。同样仅限于当前run loop。

消息转发及动态解析措施

当一个工具能接管一个消息时,会走正常的措施调用流程。但假如一个工具无法接管一个消息时,就会走消息转发机制。

消息转发机制基础上分为三个步骤:

动态措施解析

备用接管者

完备转发

详细流程可参考Objective-C Runtime 运行时之三:措施与消息,《Effective Objective-C 2.0》一书的第12小节也有具体描述。在此我们只先容一下NSObject类为实现消息转发供给的措施。

首先,对付动态措施解析,NSObject供给了以下两个措施来处置惩罚:

+ (BOOL)resolveClassMethod:(SEL)name

+ (BOOL)resolveInstanceMethod:(SEL)name

从措施名我们可以看出,

resolveClassMethod:是用于动态解析一个类措施;

而resolveInstanceMethod:是用于动态解析一个实例措施。

我们知道,一个Objective-C措施是着实是一个C函数,它至少带有两个参数,即self和_cmd。我们应用class_addMethod函数,可以给类添加一个措施。我们以resolveInstanceMethod:为例,假如要给工具动态添加一个实例措施,则可以如下处置惩罚:

void dynamicMethodIMP(id self, SEL _cmd)

{

// implementation ....

}

+ (BOOL) resolveInstanceMethod:(SEL)aSEL

{

if (aSEL == @selector(resolveThisMethodDynamically))

{

class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");

return YES;

}

return [super resolveInstanceMethod:aSel];

}

其次,对付备用接管者,NSObject供给了以下措施来处置惩罚:

- (id)forwardingTargetForSelector:(SEL)aSelector

该措施返回未被接管消息最先被转发到的工具。假如一个工具实现了这个措施,并返回一个非空的工具(且非工具本身),则这个被返回的工具成为消息的新接管者。别的假如在非根类里面实现这个措施,假如对付给定的selector,我们没有可用的工具可以返回,则应该调用父类的措施实现,并返回其结果。

着末,对付完备转发,NSObject供给了以下措施来处置惩罚

- (void)forwardInvocation:(NSInvocation *)anInvocation

当前面两步都无法处置惩罚消息时,运行时系统便会给接管者着末一个时机,将其转发给其它代理工具来处置惩罚。这主如果经由过程创建一个表示消息的NSInvocation工具并将这个工具算作参数通报给forwardInvocation:措施。我们在forwardInvocation:措施中可以选择将消息转发给其它工具。

在这个措施中,主如果必要做两件事:

找到一个能处置惩罚anInvocation调用的工具。

将消息以anInvocation的形式发送给工具。anInvocation将掩护调用的结果,而运行时则会将这个结果返回给消息的原始发送者。

这一历程如下所示:

-(void)forwardInvocation:(NSInvocation *)invocation

{

SEL aSelector = [invocation selector];

if ([friend respondsToSelector:aSelector])

[invocation invokeWithTarget:friend];

else

[super forwardInvocation:invocation];

}

当然,对付一个非根类,假如照样无法处置惩罚消息,则应该调用父类的实现。而NSObject类对付这个措施的实现,只是简单地调用了doesNotRecognizeSelector:。它不再转发任何消息,而是抛出一个非常。doesNotRecognizeSelector:的声明如下:

- (void)doesNotRecognizeSelector:(SEL)aSelector

运行时系统在工具无法处置惩罚或转发一个消息时会调用这个措施。这个措施激发一个NSInvalidArgumentException非常并天生一个差错消息。

任何doesNotRecognizeSelector:消息平日都是由运行时系统来发送的。不过,它们可以用于阻拦一个措施被承袭。例如,一个NSObject的子类可以按以下要领来重写copy或init措施以阻拦承袭:

- (id)copy

{

[self doesNotRecognizeSelector:_cmd];

}

这段代码阻拦子类的实例相应copy消息或阻拦父类转发copy消息—虽然respondsToSelector:仍旧申报接管者可以造访copy措施。

当然,假如我们要重写doesNotRecognizeSelector:措施,必须调用super的实现,或者在实现的着末激发一个NSInvalidArgumentException非常。它代表工具不能相应消息,以是老是应该激发一个非常。

获取措施信息

在消息转发的着末一步中,forwardInvocation:参数是一个NSInvocation工具,这个工具必要获取措施署名的信息,而这个署名信息便是从methodSignatureForSelector:措施中获取的。

该措施的声明如下:

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

这个措施返回包孕措施描述信息的NSMethodSignature工具,假如找不到措施,则返回nil。假如我们的工具包孕一个代理或者工具能够处置惩罚它没有直接实现的消息,则我们必要重写这个措施来返回一个相宜的措施署名。

对应于实例措施,当然还有一个处置惩罚类措施的响应措施,其声明如下:

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector

别的,NSObject类供给了两个措施来获取一个selector对应的措施实现的地址,如下所示:

- (IMP)methodForSelector:(SEL)aSelector

+ (IMP)instanceMethodForSelector:(SEL)aSelector

获取到了措施实现的地址,我们就可以直接将IMP以函数形式来调用。

对付methodForSelector:措施,假如接管者是一个工具,则aSelector应该是一个实例措施;假如接管者是一个类,则aSelector应该是一个类措施。

对付instanceMethodForSelector:措施,其只是向类工具索取实例措施的实现。假如接管者的实例无法相应aSelector消息,则孕育发生一个差错。

测试类

对付类的测试,在NSObject类中定义了两个措施,此中类措施instancesRespondToSelector:用于测试接管者的实例是否相应指定的消息,其声明如下:

+ (BOOL)instancesRespondToSelector:(SEL)aSelector

假如aSelector消息被转发到其它工具,则类的实例可以接管这个消息而不会激发差错,纵然该措施返回NO。

为了扣问类是否能相应特定消息(留意:不是类的实例),则应用这个措施,而不应用NSObject协议的实例措施respondsToSelector:。

NSObject还供给了一个措施来查看类是否采纳了某个协议,其声明如下:

+ (BOOL)conformsToProtocol:(Protocol *)aProtocol

假如一个类直接或间接地采纳了一个协议,则我们可以说这个类实现了该协议。我们可以看看以下这个例子:

@protocol AffiliationRequests

@interface MyClass : NSObject 南峰子的技巧博客

您可能还会对下面的文章感兴趣: