美文网首页
iOS NSProxy

iOS NSProxy

作者: 搬砖的crystal | 来源:发表于2021-09-16 16:05 被阅读0次
NS_ROOT_CLASS
@interface NSProxy <NSObject>{
    Class   isa;
}

NSProxy是一个实现了NSObject协议的根类。

苹果的官方文档是这样描述的:
NSProxy 是一个抽象基类,它为一些表现的像是其它对象替身或者并不存在的对象定义API。一般的,发送给代理的消息被转发给一个真实的对象或者代理本身引起加载(或者将本身转换成)一个真实的对象。NSProxy的基类可以被用来透明的转发消息或者耗费巨大的对象的lazy初始化。

NSProxy实现了包括NSObject协议在内基类所需的基础方法,但是作为一个抽象的基类并没有提供初始化的方法。它接收到任何自己没有定义的方法他都会产生一个异常,所以一个实际的子类必须提供一个初始化方法或者创建方法,并且重载forwardInvocation:方法和methodSignatureForSelector:方法来处理自己没有实现的消息。

1.消息转发和NSObject区别

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

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

2.使用
(1)伪多重继承
//
//  DJNewModel.h
//  DJTestDemo
//
//  Created by admin on 2021/9/16.
//

#import <Foundation/Foundation.h>

@interface DJNewModel : NSProxy
//转换为NSObject的方法
- (id)transformToObject:(NSObject *)object;

@end

//
//  DJNewModel.m
//  DJTestDemo
//
//  Created by admin on 2021/9/16.
//

#import "DJNewModel.h"

@interface DJNewModel()

@property (nonatomic,strong)NSObject *object;

@end

@implementation DJNewModel

- (id)transformToObject:(NSObject *)object{
    self.object = object;
    return self.object;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    NSMethodSignature *methodSignature;
    if (self.object) {
       methodSignature = [self.object methodSignatureForSelector:sel];

    }else{
       methodSignature = [super methodSignatureForSelector:sel];
    }
    return methodSignature;
}

- (void)forwardInvocation:(NSInvocation *)invocation{
    if (self.object) {
        [invocation setTarget:self.object];
        [invocation invoke];
    }
}

@end

可以使DJNewModel变成任何一个类,并调用该类的方法

    DJBMWModel *bmw = [[DJBMWModel alloc]init];
    DJNewModel *new = [DJNewModel alloc];
    [new transformToObject:bmw];
    [new performSelector:@selector(fastMethod)];
协议实现多继承

协议主要是用来提出类应遵守的标准,但其特性也可用来实现多继承。一个类可以遵守多个协议,也即实现多个协议的方法,以此来达到多继承的效果。
概念上的单继承和多继承应该是继承父类的属性和方法,并且不经重写即可使用,但通过协议实现多继承有如下不同:
(1)子类需要实现协议方法
(2)由于协议无法定义属性,所以该方法只能实现方法的多继承

#import <Foundation/Foundation.h>

@protocol DJProtocolOne <NSObject>

-(void)protocolOneMethod;

@end
#import <Foundation/Foundation.h>

@protocol DJProtocolTwo <NSObject>

-(void)protocolTwoMethod;

@end
#import "DJClass.h"
#import "DJProtocolOne.h"
#import "DJProtocolTwo.h"
@interface DJClass ()<DJProtocolOne,DJProtocolTwo>

@end

@implementation DJClass

-(void)protocolOneMethod{
    
}

-(void)protocolTwoMethod{
    
}

@end
(2)AOP

AOP(Aspect Oriented Programming),它是可以通过预编译方式和运行期动态代理实现再不修改源代码的情况下给程序动态添加功能的一种技术。因为OC的动态语言特性,所以再OC里实现AOP也有多种方式,比如使用Runtime的swizzle method机制来实现方法替换从而达到Hook的目的。

OC的动态语言的核心部分应该就是objc_msgSend方法的调用了。该函数的声明大致如下:
id objc_msgSend(id self, SEL _cmd, ...)
其中第一个参数是接受消息的target,第二个参数是要执行的selector,也就是我们要调用的方法,后面可以接若干个要传给selector的参数。
那只要我们能够Hook到对某个对象的objc_msgSend的调用,并且可以修改其参数甚至于修改成任意其他selector的IMP,我们就实现了AOP。

#import <Foundation/Foundation.h>

@interface DJNewModel : NSProxy{
//再内部hold住一个要hook的对象
id _innerObject;
}
+(instancetype)proxyWithObj:(id)object;

@end
//
//  DJNewModel.m
//  DJTestDemo
//
//  Created by admin on 2021/9/16.
//

#import "DJNewModel.h"

@implementation DJNewModel

+(instancetype)proxyWithObj:(id)object{
    DJNewModel *proxy = [DJNewModel alloc];
    //hold住要hook的对象
    proxy->_innerObject = object;
    //注意返回的值是Proxy对象
    return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
    //这里可以返回任何NSMethodSignature对象,你也可以完全自己构造一个
    return [_innerObject methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation{
    if([_innerObject respondsToSelector:invocation.selector]){
        NSString *selectorName = NSStringFromSelector(invocation.selector);
        NSLog(@"Before calling %@",selectorName);
        [invocation retainArguments];
        NSMethodSignature *sig = [invocation methodSignature];
        //获取参数个数,注意再本例里这里的值是3,为什么呢?
        //对,就是因为objc_msgSend的前两个参数是隐含的
        NSUInteger cnt = [sig numberOfArguments];
        //本例只是简单的将参数和返回值打印出来
        for (int i = 0; i < cnt; i++) {
            const char * type = [sig getArgumentTypeAtIndex:i];
            if(strcmp(type, "@") == 0){
                NSObject *obj;
                [invocation getArgument:&obj atIndex:i];
                //这里输出的是:"parameter (0)'class is MyProxy"
                //也证明了这是objc_msgSend的第一个参数
                NSLog(@"parameter (%d)'class is %@",i,[obj class]);
            }
            else if(strcmp(type, ":") == 0){
                SEL sel;
                [invocation getArgument:&sel atIndex:i];
                //这里输出的是:"parameter (1) is barking:"
                //也就是objc_msgSend的第二个参数
                NSLog(@"parameter (%d) is %@",i,NSStringFromSelector(sel));
            }
            else if(strcmp(type, "q") == 0){
                int arg = 0;
                [invocation getArgument:&arg atIndex:i];
                //这里输出的是:"parameter (2) is int value is 4"
                //稍后会看到我们再调用barking的时候传递的参数就是4
                NSLog(@"parameter (%d) is int value is %d",i,arg);
            }
        }
        //消息转发
        [invocation invokeWithTarget:_innerObject];
        const char *retType = [sig methodReturnType];
        if(strcmp(retType, "@") == 0){
            NSObject *ret;
            [invocation getReturnValue:&ret];
            //这里输出的是:"return value is wang wang!"
            NSLog(@"return value is %@",ret);
        }
        NSLog(@"After calling %@",selectorName);
    }
}

@end

相关文章

  • NSProxy

    NSProxy——少见却神奇的类 - IOS - 伯乐在线

  • iOS NSProxy

    NSProxy类在分布式对象架构中是很重要的。由于作用比较特别,NSProxy在Cocoa程序中出现频率很低。 N...

  • iOS -NSProxy

    可以看到,它遵守了 NSObject 协议,并且第一个 Ivar 是一个 isa 指针,因此它完全是可以拿来当一个...

  • iOS | NSProxy

    Objective-C作为一种动态消息型语言,其机制不同于Java ,C#等编译型语言.它将数据类型的确定等工作...

  • iOS:NSProxy

    OC 中一个类只有一个父类,这就是单一继承,但是我们可以用协议和NSProxy实现多继承。 1、 protocol...

  • iOS NSProxy

    NSProxy是一个实现了NSObject协议的根类。 苹果的官方文档是这样描述的:NSProxy 是一个抽象基类...

  • iOS 解决NSTimer的循环引用问题

    iOS 13以后可以通过block解决target的强引用问题,如果程序兼顾iOS13以下,那么使用NSProxy...

  • iOS NSProxy探究

    什么是NSProxy NSProxy是和NSObject同级的一个类,可以说它是一个虚拟类,它只是实现了

  • ios开发-NSProxy

    作用:与NSObject属同一个级别,负责将消息转发到真正的target的代理类,实现类似于多继承的功能。 打个比...

  • iOS NSProxy使用

    简介:通过NSProxy 可以实现类的"伪多继承",demo中KLProxy通过拦截方法修改了cat和dog本来的...

网友评论

      本文标题:iOS NSProxy

      本文链接:https://www.haomeiwen.com/subject/plbrgltx.html