今天看FLAnimatedImage時(shí)無(wú)意只發(fā)現(xiàn)一個(gè)proxy類(lèi)FLWeakProxy,里面有這樣的代碼:
[self.weakProxy performSelector:@selector(resetFrameCacheSizeMaxInternal) withObject:nil afterDelay:kResetDelay];
因?yàn)閜erformSelector : afterDelay會(huì)強(qiáng)引用當(dāng)前類(lèi),為了防止內(nèi)存泄露才用的proxy。
1 .先貼出代碼 FLWeakProxy.h文件:
@interface FLWeakProxy : NSProxy
+ (instancetype)weakProxyForObject:(id)targetObject;
@end
2.FLWeakProxy.m文件
#import "FLWeakProxy.h"
@interface FLWeakProxy ()
@property (nonatomic, weak) id target;
@end
@implementation FLWeakProxy
#pragma mark Life Cycle
// This is the designated creation method of an `FLWeakProxy` and
// as a subclass of `NSProxy` it doesn't respond to or need `-init`.
+ (instancetype)weakProxyForObject:(id)targetObject
{
FLWeakProxy *weakProxy = [FLWeakProxy alloc];
weakProxy.target = targetObject;
return weakProxy;
}
#pragma mark Forwarding Messages
- (id)forwardingTargetForSelector:(SEL)selector
{
// Keep it lightweight: access the ivar directly
return _target;
}
#pragma mark - NSWeakProxy Method Overrides
#pragma mark Handling Unimplemented Methods
- (void)forwardInvocation:(NSInvocation *)invocation
{
// Fallback for when target is nil. Don't do anything, just return 0/NULL/nil.
// The method signature we've received to get here is just a dummy to keep `doesNotRecognizeSelector:` from firing.
// We can't really handle struct return types here because we don't know the length.
void *nullPointer = NULL;
[invocation setReturnValue:&nullPointer];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
// We only get here if `forwardingTargetForSelector:` returns nil.
// In that case, our weak target has been reclaimed. Return a dummy method signature to keep `doesNotRecognizeSelector:` from firing.
// We'll emulate the Obj-c messaging nil behavior by setting the return value to nil in `forwardInvocation:`, but we'll assume that the return value is `sizeof(void *)`.
// Other libraries handle this situation by making use of a global method signature cache, but that seems heavier than necessary and has issues as well.
// See https://www.mikeash.com/pyblog/friday-qa-2010-02-26-futures.html and https://github.com/steipete/PSTDelegateProxy/issues/1 for examples of using a method signature cache.
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
@end
_target是弱引用, 為了打破循環(huán)。然后它在forwardingTargetForSelector把所有方法調(diào)用傳給了_target。- 當(dāng)_target被釋放后,如果方法被調(diào)用那么肯定會(huì)報(bào)
doesNotRecognizeSelector,所以要重寫(xiě)forwardInvocation和methodSignatureForSelector兩個(gè)方法,它們響應(yīng)了method,但是不會(huì)做任何處理是返回nil,只要不報(bào)錯(cuò)就行。
那么我們?nèi)绾问褂媚兀?/h3>
主要有兩個(gè)場(chǎng)景,定時(shí)器NSTimer和performSelector。
1.定時(shí)器NSTimer
_weakProxy = [FLWeakProxy weakProxyForObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:_weakProxy selector:@selector(timeUpdate) userInfo:nil repeats:YES];
2.performSelector :delay (沒(méi)成功)
代碼如下:
_weakProxy = [FLWeakProxy weakProxyForObject:self];
[_weakProxy performSelector:@selector(delayMethod) withObject:nil afterDelay:4];
結(jié)果不起作用!我認(rèn)為:雖然是_weakProxy調(diào)用的方法,但是真正是_target調(diào)用的performSelector,所以runloop還是強(qiáng)引用了_target,沒(méi)有起到打破循環(huán)的目的!
解決方法:
如果讓weakProxy繼承的
NSProxy改成NSObject就可以完美解決這個(gè)問(wèn)題,因?yàn)镹SObject本身有performSelector方法,所以是_weakProxy調(diào)用的performSelector,等4秒之后調(diào)用delayMethod時(shí)才會(huì)調(diào)用_target執(zhí)行,這樣是沒(méi)有問(wèn)題的!但是我查了一下NSProxy和NSObject的區(qū)別,都說(shuō)NSProxy是最適合做代理的,而且用NSObject之后,NSObject的category里的方法是無(wú)法正常調(diào)用的。像下面的代碼:
NSLog(@"%@",[proxyA valueForKey:@"length"]);//NSProxy
NSLog(@"%@",[proxyB valueForKey:@"length"]);//NSObject
結(jié)果是不一樣的,因?yàn)関alueForKey是catergory里的方法,無(wú)法被調(diào)用。
結(jié)語(yǔ): 如果只針對(duì)NSTimer時(shí),用NSProxy是沒(méi)有任何問(wèn)題的。但是對(duì)于performSelector不知如何是好,但是像FLAnimatedImage這么牛的第三方,按理說(shuō)不會(huì)出現(xiàn)這種嚴(yán)重的bug的。。。難道是我理解錯(cuò)了嗎?歡迎大家評(píng)論~~~~~~~~~