探秘NSProxy

iOS优化

之前一直知道有一个与NSObject平级的类存在,但是日常的开发中好像一直都没有用到过。那么他存在的意义究竟是什么呢?这篇文章我们一起来探秘NSProxy。

先看代码

我们先看一下NSProxy这个类的声明

1
2
3
4
5
@interface NSProxy <NSObject> {
Class isa;
}

@end

这个类遵守了<NSObject>这个协议,我们来看一下这个协议究竟都做了什么呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@protocol NSObject

- (BOOL)isEqual:(id)object;
@property (readonly) NSUInteger hash;

@property (readonly) Class superclass;
- (Class)class OBJC_SWIFT_UNAVAILABLE("use 'type(of: anObject)' instead");
- (instancetype)self;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

- (BOOL)isProxy;

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

- (BOOL)respondsToSelector:(SEL)aSelector;

- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;

- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

@property (readonly, copy) NSString *description;
@optional
@property (readonly, copy) NSString *debugDescription;

@end

这个协议主要定义了一些类的一些基本方法。

这样看 感觉跟NSObject没什么太大的区别。我们在进一步看看这个类中的方法。

init方法

在跟NSObject做了简单对比之后发现,NSProxy没有init方法。我们都知道NSObject在alloc方法内申请内存空间 init方法进行初始化。 从这个角度来看的话NSProxy是没有办法被直接创建和初始化的。

方法调用

对于class NSObject而言,接收到消息后先去自身的方法列表里找匹配的selector,如果找不到,会沿着继承体系去superclass的方法列表找;如果还找不到,先后会经过+resolveInstanceMethod:-forwardingTargetForSelector:处理,处理失败后,才会到-methodSignatureForSelector:/-forwardInvocation:进行最后的挣扎。更详细的叙述,详见NSObject的消息转发机制。

但对于NSProxy,接收unknown selector后,直接回调-methodSignatureForSelector:/-forwardInvocation:,消息转发过程比class NSObject要简单得多。

API 注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//一个抽象的超类,为那些以替身的身份存在的对象或者还没存在的对象定义了一套API。
An abstract superclass defining an API for objects that act as stand-ins for
other objects or for objects that don’t exist yet.

//通常,向NSProxy发送一条消息会被转发给一个对象或者会让NSProxy去加载一个对象.NSProxy的子类可以用来实现透明的消息分发(例如NSDistantObject) 或者对创建昂贵的对象进行懒加载。

// 这里的意思大概是NSProxy的子类可以用来做消息的分发
Typically, a message to a proxy is forwarded to the real object or causes the
proxy to load (or transform itself into) the real object. Subclasses of
NSProxy can be used to implement transparent distributed messaging (for
example, NSDistantObject) or for lazy instantiation of objects that are
expensive to create.


//NSProxy实现了作为根类的一些基本的方法,包括在NSObject协议中定义的。然而,作为一个抽象类他没有提供一个实例化方法,在接收到任何他没有实现的方法时都会抛出异常。
NSProxy implements the basic methods required of a root class, including those
defined in the NSObject protocol. However, as an abstract class it doesn’t
provide an initialization method, and it raises an exception upon receiving
any message it doesn’t respond to.

//因此 一个具体的子类必提供一个实例化方法或者创建方法。并且覆盖`forwardInvocation`方法和`methodSignatureForSelector`方法来处理类本身没有实现的方法。
A concrete subclass must therefore provide an initialization or creation
method and override the forwardInvocation: and methodSignatureForSelector:
methods to handle messages that it doesn’t implement itself.

//子类中`forwardInvocation`的实现中应该做任何需要的来处理invocation,比如通过网络转发这个invocation,或者加载一个的对象并将这个invocation转发给他。
A subclass’s implementation of forwardInvocation: should do whatever is
needed to process the invocation, such as forwarding the invocation over the
network or loading the real object and passing it the invocation.

//`methodSignatureForSelector`是用来获取给定消息的参数类型;在子类的实现中可以获取这个将要被转发的消息的参数类型。同时应该创建一个相对应的NSMethodSignature对象。
methodSignatureForSelector: is required to provide argument type information
for a given message; a subclass’s implementation should be able to determine
the argument types for the messages it needs to forward and should construct
an NSMethodSignature object accordingly. See the NSDistantObject,
NSInvocation, and NSMethodSignature class specifications for more
information.

通过上面的这段解释,我们能大概的了解到NSProxy的作用:

因为NSProxy没有init方法,因此所有的操作必须是通过子类实现,而且 因为子类也同样没有init方法,那就决定了NSProxy以及子类都无法直接去相应某一个方法。因此NSProxy的子类一般是讲调用的方法转发给对应的某些对象。因此其扮演的实际上是一个分发者的角色。

示例

为了对比他和NSObject的区别 我们这里创建两个类分别继承自NSObject和NSProxy.

1
2
3
@interface ProxyObject : NSProxy
- (instancetype)initWithObject:(id)object;
@end
1
2
3
@interface NormalObject : NSObject
- (instancetype)initWithObject:(id)object;
@end

我们在这两个类中都创建了一个对象.当当前类对象没有实现对应方法的时候,统一转发给这个对象。

参考文章