NSProxy

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

1、 protocol

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

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


#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与消息转发机制,也可参考:

NSTimer是一个需要添加到Runloop里的类,对于一个不会自动停止的Timer,你需要调用invalidate方法来手动断开这个Timer。否则,引用Timer的Controller或者其他类,就会出现循环引用而无法释放掉。  
举个例子,在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.1 实现延迟初始化(Lazy Initialization)

使用场景


第一种情况

    在 [SomeClass lazy] 之后调用 doSomthing,首先进入 forwardingTargetForSelector,_object 为 nil 并且不是 init 开头的方法的时候会调用 init 初始化对象,然后将消息转发给代理对象 _object;


第二种情况,

    在 [SomeClass lazy] 之后调用 initWithXXX:,首先进入 forwardingTargetForSelector 返回 nil,然后进入 methodSignatureForSelector: 和 forwardInvocation: 保存自定义初始化方法的调用,最后调用 doSomthing,进入 forwardingTargetForSelector,_object 为 nil 并且不是 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)];

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值