美文网首页
iOS:NSProxy

iOS:NSProxy

作者: 街角仰望 | 来源:发表于2020-04-24 16:18 被阅读0次

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

1、 protocol

先说协议,协议我们用的最多的地方就是代理,其实代理不叫代理,叫委托。

比如我有两个协议,分别是PersonYFChild

#import <Foundation/Foundation.h>
 
@protocol YFPerson <NSObject>
@required
@property (nonatomic,copy,readonly)NSString *name;
 
@optional
- (void)sleep;
@end
#import <Foundation/Foundation.h>
 
@protocol Child <NSObject>
@required
- (NSString *)nickname;
@optional
- (void)study;
@end

那么,我在新创建的一个 YFStudent类中,只要遵守上面两个协议,实现协议里的方法,就可以在一个类中,实现多个协议中的方法了。

@interface Student : NSObject<Child,Person>
 
@end
 
@implementation Student
- (NSString *)nickname
{
    return @"阿满";
}
- (void)sleep{
    NSLog(@"睡觉");
}
- (void)study{
    NSLog(@"学习");
}
@end

调用

Student *student = [[Student alloc]init];
student.name = @"曹操";
[student study];
[student sleep];
 NSLog(@"%@",student.nickname);

2、NSProxy

什么是NSProxy

  • NSProxy是一个抽象的基类,是根类,与NSObject类似
  • NSProxyNSObject都实现了<NSObject>协议
  • 提供了消息转发的通用接口

如何使用NSProxy来转发消息?

1.我先设置一个类WeakProxy,继承自NSProxy
2.为WeakProxy设置一个NSObject属性
3.自定义一个转换方法,相当于给NSObject属性赋值
4.然后通过这个属性获得调用方法的方法签名methodSignatureForSelector:
5.为调用设置目标forwardInvocation:

注:methodSignatureForSelector:方法,返回的是一个NSMethodSignature类型,来描述给定selector的参数和返回值类型。返回nil,表示proxy不能识别指定的selector。所有的NSObject也响应此消息。 forwardInvocation:方法将消息转发到对应的对象上

2.1 避免循环引用

一些开源项目中使用NSProxy来避免循环引用,用在NSTimer或者CADisplayLink

以下内容来自NSProxy与消息转发机制,也可参考:

  • 利用NSProxy解决NSTimer内存泄漏问题

NSTimer是一个需要添加到Runloop里的类,对于一个不会自动停止的Timer,你需要调用invalidate方法来手动断开这个Timer。否则,引用TimerController或者其他类,就会出现循环引用而无法释放掉。

举个例子,在Controller中,添加Timer很常见,比如

#import "SecondViewController.h"
@interface SecondViewController ()
 
@property (strong,nonatomic)NSTimer * timer;
 
@end
@implementation SecondViewController
 
- (void)viewDidLoad{
    [super viewDidLoad];
    self.timer = [NSTimer timerWithTimeInterval:1
                                         target:self
                                       selector:@selector(timerInvoked:)
                                       userInfo:nil
                                        repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerInvoked:(NSTimer *)timer{
    NSLog(@"1");
}
- (void)dealloc{
    NSLog(@"Dealloc");
}
@end

假如我Push这样一个SecondViewController,然后pop。你会发现Controller没有被释放,timer也没有被取消。

我们可以在dealloc中,调用Timer取消吗?比如

- (void)dealloc{
    [self.timer invalidate];
    NSLog(@"Dealloc");
}

当然不行,因为Controller根本没有被释放,dealloc方法根本不会调用。 当然破坏这种循环引用的方式有很多种。本文主要讲解如何用NSProxy来破坏。

我们写一个WeakProxy来实现弱引用

1.为外界暴露一个变身方法:

@interface WeakProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
- (instancetype)initWithTarget:(id)target;
@end

2.设置一个NSObject属性

#import "WeakProxy.h"
@interface WeakProxy ()
@property (weak,nonatomic,readonly)id target;
 
@end

3.实现变身方法

- (instancetype)initWithTarget:(id)target{
    _target = target;
    return self;
}
+ (instancetype)proxyWithTarget:(id)target{
    return [[self alloc] initWithTarget:target];
}

4.重写- (NSMethodSignature *)methodSignatureForSelector:(SEL)see方法获得方法签名

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    return [self.target methodSignatureForSelector:aSelector];
}

5.重写- (void)forwardInvocation:(NSInvocation *)invocation方法改变调用对象,也就是说,让消息实际上发给真正的实现这个方法的类

- (void)forwardInvocation:(NSInvocation *)invocation{
    SEL sel = [invocation selector];
    if ([self.target respondsToSelector:sel]) {
        [invocation invokeWithTarget:self.target];
    }
}
- (BOOL)respondsToSelector:(SEL)aSelector{
    return [self.target respondsToSelector:aSelector];
}

调用

self.timer = [NSTimer timerWithTimeInterval:1 target:[WeakProxy proxyWithTarget:self] selector:@selector(timerInvoked:) userInfo:nil repeats:YES];
2.2 实现延迟初始化(Lazy Initialization)

使用场景:

第一种情况:
[SomeClass lazy]之后调用doSomthing,首先进入forwardingTargetForSelector_objectnil并且不是init开头的方法的时候会调用init初始化对象,然后将消息转发给代理对象_object

第二种情况:
[SomeClass lazy]之后调用initWithXXX:,首先进入forwardingTargetForSelector返回nil,然后进入 methodSignatureForSelector:forwardInvocation:保存自定义初始化方法的调用,最后调用doSomthing,进入forwardingTargetForSelector_objectnil并且不是init开头的方法的时候会调用自定义初始化方法,然后将消息转发给代理对象 _object

SomeClass *object = [SomeClass lazy];
// other thing ...
[object doSomething];// 在这里,object 才会调用初始化方法,然后调用 doSomething
2.3实现多继承
#import <Foundation/Foundation.h>
 
@interface NCProxy : NSProxy
 
- (id)transformToObject:(NSObject *)object;
 
@end
 
#import "NCProxy.h"
 
@interface NCProxy ()
 
@property (nonatomic,strong)NSObject *object;
 
@end
@implementation NCProxy
- (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

Person类

#import "Person.h"
 
@interface Person ()
 
@property (nonatomic,copy)NSString *name;
 
@end
 
@implementation Person
 
- (void)eat
{
    NSLog(@"%@正在吃饭",self.name);
}
 
@end

Student类

#import "Student.h"
 
@interface Student ()
 
@property (nonatomic,copy)NSString *studentNum;
 
@end
 
@implementation Student
 
- (void)study
{
    NSLog(@"哥正在学习");
}
 
@end

调用

Person *person = [[Person alloc]init];
Student *student = [[Student alloc]init];
//为 YFProxy 开辟一块内存空间
NCProxy *proxy = [NCProxy alloc];
//变身
[proxy transformToObject:person];
//这样就可以自由自在地调用 Person 类的方法了,person 类的方法甚至是真私有的,都可以调得到,虽然报警告了
[proxy performSelector:@selector(setName:) withObject:@"小明"];
[proxy performSelector:@selector(eat)];
//再变
[proxy transformToObject:student];
//这样又可以调 Student类的方法了
[proxy performSelector:@selector(study)];

参考:https://blog.csdn.net/shubinniu/article/details/80895450

相关文章

  • 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/trmfwhtx.html