一. 使用NSProxy解决NSTimer、CADisplayLink等循环引用
如下使用NSTimer
如果不做任何处理会导致内存泄露。为了解决self
和timer
互相强引用问题,可以再某个时间点调用 [self.timer invalidate];self.timer = nil;
,这样是可以解决内存泄漏的,但是不太灵活。
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
使用下面这种方式可以很灵活的解决循环引用问题。
1. ViewController.m类
@interface ViewController ()
@property (strong, nonatomic) NSTimer *timer;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[YHProxy proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
}
- (void)timerTest
{
NSLog(@"%s", __func__);
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[self.timer invalidate];
}
@end
2. YHProxy类
#import <Foundation/Foundation.h>
@interface YHProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
#import "YHProxy.h"
@implementation YHProxy
+ (instancetype)proxyWithTarget:(id)target
{
// NSProxy对象不需要调用init,因为它本来就没有init方法
YHProxy *proxy = [YHProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end
NSProxy
这个类很简单,他不会进行方法查找,也不会进行动态消息解析,而是直接调用methodSignatureForSelector
和forwardInvocation
,所以不存在效率低的问题。
二. NSProxy对象调用isKindOfClass
1. YHProxy1类
#import <Foundation/Foundation.h>
@interface YHProxy1 : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
#import "YHProxy1.h"
@implementation YHProxy1
+ (instancetype)proxyWithTarget:(id)target
{
YHProxy1 *proxy = [[YHProxy1 alloc] init];
proxy.target = target;
return proxy;
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.target;
}
@end
2. main函数
int main(int argc, char * argv[]) {
@autoreleasepool {
ViewController *vc = [[ViewController alloc] init];
YHProxy *proxy = [YHProxy proxyWithTarget:vc];
YHProxy1 *proxy1 = [YHProxy1 proxyWithTarget:vc];
NSLog(@"%d %d",
[proxy isKindOfClass:[ViewController class]],
[proxy1 isKindOfClass:[ViewController class]]);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
3. 结果:
1 0
4. 分析
YHProxy1
继承自NSObject
,YHProxy1
的所有父类和[ViewController class]
都不是一种类型,所以结果为0
。
[proxy isKindOfClass:[ViewController class]]
为什么为1
呢?因为NSProxy
在isKindOfClass
内部直接调用了- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel、- (void)forwardInvocation:(NSInvocation *)invocation
,修改了target
导致的,相当于是vc
调用了isKindOfClass
,所以[proxy isKindOfClass:[ViewController class]]
等于[vc isKindOfClass:[ViewController class]]
结果为1。
通过GNUStep可以看到伪代码。