解決NSTimer自動(dòng)釋放的問(wèn)題

場(chǎng)景: 使用NSTimer會(huì)產(chǎn)生循環(huán)引用, 所以, 當(dāng)對(duì)象應(yīng)該被銷(xiāo)毀的時(shí)候, 需要首先手動(dòng)移除NSTimer, 這樣, 該對(duì)象才對(duì)被銷(xiāo)毀. 手動(dòng)設(shè)置timer = nil.
timer之所以不能自動(dòng)像其他對(duì)象跟隨對(duì)象釋放而釋放

解決方案: 不讓定時(shí)器強(qiáng)引用對(duì)象.

問(wèn)題出在這里, 把target設(shè)置成self, 出現(xiàn)了循環(huán)引用. 如果target不設(shè)置成self, 就不會(huì)出現(xiàn)循環(huán)引用了.

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];

創(chuàng)建另外一個(gè)對(duì)象作為定時(shí)間消息的接受者, 但是不實(shí)現(xiàn)@selector(timerTest), 然后通過(guò)消息轉(zhuǎn)發(fā), 再發(fā)消息轉(zhuǎn)發(fā)到當(dāng)前對(duì)象中執(zhí)行.

@interface ProxyObj : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation ProxyObj

+ (instancetype)proxyWithTarget:(id)target
{
    MJProxyObj *proxy = [[MJProxyObj alloc] init];
    proxy.target = target;
    return proxy;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    return self.target;
}
@end

- (id)forwardingTargetForSelector:(SEL)aSelector 因?yàn)镻roxyObj最為定時(shí)器消息的接受者, 但是這個(gè)接受者并未實(shí)現(xiàn)定時(shí)器需要執(zhí)行的方法, 所以會(huì)執(zhí)行消息轉(zhuǎn)發(fā), 把這個(gè)消息轉(zhuǎn)發(fā)到能夠處理該消息的對(duì)象里, 很明顯, 能夠處理消息的就是ProxyObj.target

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[ProxyObj proxyWithTarget:self] selector:@selector(timerTest) userInfo:nil repeats:YES];
}

- (void)timerTest
{
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    NSLog(@"%s", __func__);
    [self.timer invalidate];
}

@end

這樣就可以辦到timer不引用當(dāng)前對(duì)象, 不會(huì)造成循環(huán)引用. 也不用擔(dān)心timer何時(shí)釋放了.

優(yōu)化方法查找流程

繼承自NSObject的進(jìn)行消息轉(zhuǎn)發(fā)會(huì)先從方法緩存列表中找, 找不到再找當(dāng)前類(lèi)對(duì)象的方法列表中找, 找不到, 再依次往父類(lèi)中找, 最終找到NSObject的類(lèi)對(duì)象,
依然找不到, 然后拋出動(dòng)態(tài)方法解析, 動(dòng)態(tài)方法解析沒(méi)有實(shí)現(xiàn), 再拋出消息轉(zhuǎn)發(fā), 此時(shí)才會(huì)真正去調(diào)用到定時(shí)器的方法

按照目前的流程, 必定是需要消息轉(zhuǎn)發(fā)的, 但是還是先進(jìn)行一番的方法查找, 最后才進(jìn)行轉(zhuǎn)發(fā), 但是我們已經(jīng)很清楚的知道, 根本不需要進(jìn)行查找, 直接進(jìn)行轉(zhuǎn)發(fā)即可.
NSProxy: 專(zhuān)門(mén)做消息轉(zhuǎn)發(fā)的, 只要調(diào)用到NSProxy里面的方法, 就會(huì)立即執(zhí)行轉(zhuǎn)發(fā)

@interface Proxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation Proxy

+ (instancetype)proxyWithTarget:(id)target
{
    // NSProxy對(duì)象不需要調(diào)用init,因?yàn)樗緛?lái)就沒(méi)有init方法
    MJProxy *proxy = [MJProxy alloc];
    proxy.target = target;
    return proxy;
}

// NSProxy 是專(zhuān)門(mén)做消息轉(zhuǎn)發(fā)的, 只要調(diào)用到NSProxy里面的方法, 就會(huì)立即執(zhí)行下面的函數(shù)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    [invocation invokeWithTarget:self.target];
}
@end
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容