CocoaAsyncSocket 源碼學習摘要:
1. [NSObject cancelPreviousPerformRequestsWithTarget:self]
//AsyncSocket 的dealloc說明。
- (void)dealloc
{
[self close];
[theReadQueue release];
[theWriteQueue release];
[theRunLoopModes release];
[partialReadBuffer release];
[NSObject cancelPreviousPerformRequestsWithTarget:theDelegate selector:@selector(onSocketDidDisconnect:) object:self];
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[super dealloc];
}
一般我們都知道調用了[instance performSelector:@selector() withObject:nil afterDelay:]會導致instance引用計數+1。如果我們調用了[instance performSelector:@selector() withObject:nil afterDelay:]。在某些情況下我們就需要調用[NSObject cancelPreviousPerformRequestsWithTarget:instance]或者功能類似API,來取消延遲。否則有些時候會造成內存問題(實例都釋放了還在調用實例方法)導致crash,或者導致某個類延遲釋放。
問題:
如果有一個類,在類中有一個方法調用了[self performSelector:@selector() withObject:nil afterDelay:]。那么在這個類中對應的dealloc方法中是否有必要調用[NSObject cancelPreviousPerformRequestsWithTarget:self]呢?
因為延遲調用會把self引用計數+1,調用了之后,只能等延遲方法執行完或者被[NSObject cancelPreviousPerformRequestsWithTarget:self]類似方法取消掉,否則不可能執行到dealloc方法中。這樣看來dealloc方法調用[NSObject cancelPreviousPerformRequestsWithTarget:self]沒啥用。
但是最佳實踐中我還是會建議在dealloc方法最后加上[NSObject cancelPreviousPerformRequestsWithTarget:self]
理由如下:
如果我們在定義一個類,里面有方法調用了[self performSelector:@selector() withObject:nil afterDelay:]。那么在這個類的dealloc方法最后要調用[NSObject cancelPreviousPerformRequestsWithTarget:self]。因為dealloc里面一旦調用了一些方法,這些方法間接或者直接調用了[self performSelector:@selector() withObject:nil afterDelay:],那么就等待著crash吧。此時在dealloc方法最后加上[NSObject cancelPreviousPerformRequestsWithTarget:self]。問題就解決了。總之dealloc里面的[NSObject cancelPreviousPerformRequestsWithTarget:self]方法不一定有用,但是加上有益無害。
還是不太明白?請看下面代碼:
@interface Person : NSObject
@end
@implementation Person
- (void)personTest{
NSLog(@"personTest");
}
- (void)invoke{
[self performSelector:@selector(personTest) withObject:nil afterDelay:0];
}
- (void)dealloc{
[self invoke];
// [NSObject cancelPreviousPerformRequestsWithTarget:self];
}
@end
Person *person = [Person new];person實例釋放的時候必然會奔潰。
2.[NSObject performSelector]系列函數和
[NSRunLoop performSelector]系列函數區別
下面是官方API,都是為了實現延遲調用,但是你們知道他們的區別嗎?
/**************** Delayed perform ******************/
@interface NSObject (NSDelayedPerforming)
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes;
- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument;
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget;
@end
@interface NSRunLoop (NSOrderedPerform)
- (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes;
- (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg;
- (void)cancelPerformSelectorsWithTarget:(id)target;
@end
下面是CocoaAsyncSocket早期基于runLoop實現的部分代碼
代碼一:
- (BOOL)moveToRunLoop:(NSRunLoop *)runLoop{
NSAssert((theRunLoop == NULL) || (theRunLoop == CFRunLoopGetCurrent()),
@"moveToRunLoop must be called from within the current RunLoop!");
[runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
[runLoop performSelector:@selector(maybeDequeueWrite) target:self argument:nil order:0 modes:theRunLoopModes];
[runLoop performSelector:@selector(maybeScheduleDisconnect) target:self argument:nil order:0 modes:theRunLoopModes];
}
代碼二:
- (BOOL)setRunLoopModes:(NSArray *)runLoopModes{
NSAssert((theRunLoop == CFRunLoopGetCurrent()), @"setRunLoopModes must be called from within the current RunLoop!");
[self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
[self performSelector:@selector(maybeDequeueWrite) withObject:nil afterDelay:0 inModes:theRunLoopModes];
[self performSelector:@selector(maybeScheduleDisconnect) withObject:nil afterDelay:0 inModes:theRunLoopModes];
}
為了方便把下面方法定義為“方法1”
[runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes];
下面方法定義為“方法2”
[self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes];
"方法1","方法2"都干同樣一件事,就是延遲執行maybeDequeueRead調用。但是它們是有區別的。區別如下:
NSRunLoop一般是和線程相關的。一個線程最多有一個NSRunLoop也可能沒有。在這里可以將runLoop看做一個線程。
假設在“線程A”中調用moveToRunLoop,當線程A執行到moveToRunLoop方法中的[runLoop performSelector:@selector(maybeDequeueRead) target:self argument:nil order:0 modes:theRunLoopModes]這段代碼的時候。“線程A”喚起另一個線程(runLoop所在的線程)假設為“線程B”去調用maybeDequeueRead方法。實現效果和下面方法一致*- (void)performSelector:(SEL)aSelector onThread:(NSThread )thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait (只是一個基于RunLoop實現,一個基于Thread調用)。這樣就實現了多線程切換調用。
但是如果用下面方法[self performSelector:@selector(maybeDequeueRead) withObject:nil afterDelay:0 inModes:theRunLoopModes]則所有調用都在”線程A“中。
先整理到這吧,后面還會繼續分析CocoaAsyncSocket學些心得。