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
类似NSProxy
和NSObject
都实现了<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)];