利用NSProxy解決NSTimer記憶體洩漏問題
之前寫過一篇利用RunTime解決由NSTimer導致的記憶體洩漏的文章,最近和同事討論覺得這樣寫有點複雜,然後發現有NSProxy
這麼好用的根類
,根類
,根類
,沒錯NSProxy
與NSObject
一樣是根類,都遵守<NSObject>
協議。
實際上本篇用了訊息轉發的機制來避免NSTimer
記憶體洩漏的問題,無論是NSProxy
與NSObject
的派生類在Objective-C
執行時找不到訊息都會執行訊息轉發。所以這個解決方案用NSProxy
與NSObject
的子類都能實現,不過NSProxy
從類名來看是代理類專門負責代理物件轉發訊息的。相比NSObject
類來說NSProxy
更輕量級,通過NSProxy
Objective-C
間接的實現多重繼承的功能。
NSProxy
NSProxy
是一個抽象類,必須繼承例項化其子類才能使用。NSproxy
具體使用參考官方示例,在上面示例中通過訊息轉發實現了同時對NSProxy
傳送NSMutableString
和NSMutableArray
型別的訊息間接的實現了多重繼承。
關於訊息轉發
當程式執行時呼叫一個沒有實現的方法,會採用三個訊息轉發步驟如果這三個步驟都不能成功那麼此時程式會丟擲一個異常。
1.新增方法到類物件,對於例項方法呼叫respondsToSelector:
,對於類方法呼叫resolveClassMethod:
。
+(BOOL)resolveClassMethod:(SEL)sel;
-(BOOL)respondsToSelector:(SEL)aSelector;
2.查詢forwardingTargetForSelector:
方法,該方法返回一個新物件,如果返回nil
那麼將跳轉到下一步驟。
-(id)forwardingTargetForSelector:(SEL)aSelector;
3.通過methodSignatureForSelector:
方法獲取一個NSMethodSignature
型別的物件,呼叫forwardInvocation:
方法。改方法傳入一個封裝了NSMethodSignature
的NSInvocation
物件。然後該物件通過invakeWithTarget:
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)invocation;
使用NSProxy解決NSTimer記憶體洩漏
.h檔案
#import <UIKit/UIKit.h>
@interface LSYViewController : UIViewController
@end
@interface LSYProxy : NSProxy
@property (nonatomic,weak) id obj;
@end
.m檔案
#import "LSYViewController.h"
@interface LSYViewController ()
@property (nonatomic,strong) LSYProxy *proxy;
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic) NSInteger count;
@end
@implementation LSYViewController
-(void)viewDidLoad
{
[super viewDidLoad];
self.proxy = [LSYProxy alloc];
self.proxy.obj = self;
_timer = [NSTimer timerWithTimeInterval:1 target:self.proxy selector:@selector(timerEvent) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}
-(void)timerEvent
{
NSLog(@"count---%d",(int)++_count);
}
-(void)dealloc
{
NSLog(@"---dealloc----");
[_timer invalidate];
}
@end
#pragma mark - LSYProxy Implementation
@implementation LSYProxy
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig;
sig = [self.obj methodSignatureForSelector:aSelector];
return sig;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.obj];
}
@end
原理和我之前寫的利用RunTime解決由NSTimer導致的記憶體洩漏是一樣的,只不過實現不同,之前是利用關聯物件的方法來斷開NSTimer
與檢視之間的引用關係,這個是利用訊息轉發來斷開NSTimer
物件與檢視之間的引用關係。