1. 程式人生 > >利用NSProxy解決NSTimer記憶體洩漏問題

利用NSProxy解決NSTimer記憶體洩漏問題

之前寫過一篇利用RunTime解決由NSTimer導致的記憶體洩漏的文章,最近和同事討論覺得這樣寫有點複雜,然後發現有NSProxy這麼好用的根類根類根類,沒錯NSProxyNSObject一樣是根類,都遵守<NSObject>協議。
實際上本篇用了訊息轉發的機制來避免NSTimer記憶體洩漏的問題,無論是NSProxyNSObject的派生類在Objective-C執行時找不到訊息都會執行訊息轉發。所以這個解決方案用NSProxyNSObject的子類都能實現,不過NSProxy從類名來看是代理類專門負責代理物件轉發訊息的。相比NSObject類來說NSProxy更輕量級,通過NSProxy

可以幫助Objective-C間接的實現多重繼承的功能。

NSProxy

NSProxy是一個抽象類,必須繼承例項化其子類才能使用。NSproxy具體使用參考官方示例,在上面示例中通過訊息轉發實現了同時對NSProxy傳送NSMutableStringNSMutableArray型別的訊息間接的實現了多重繼承。
關於訊息轉發
當程式執行時呼叫一個沒有實現的方法,會採用三個訊息轉發步驟如果這三個步驟都不能成功那麼此時程式會丟擲一個異常。

1.新增方法到類物件,對於例項方法呼叫respondsToSelector:,對於類方法呼叫resolveClassMethod:

+(BOOL)resolveClassMethod:(SEL)sel;
-(BOOL)respondsToSelector:(SEL)aSelector;

2.查詢forwardingTargetForSelector:方法,該方法返回一個新物件,如果返回nil那麼將跳轉到下一步驟。

-(id)forwardingTargetForSelector:(SEL)aSelector;

3.通過methodSignatureForSelector:方法獲取一個NSMethodSignature型別的物件,呼叫forwardInvocation:方法。改方法傳入一個封裝了NSMethodSignatureNSInvocation物件。然後該物件通過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物件與檢視之間的引用關係。