NSOperation 子类性能或泄漏
NSOperation Subclass performance or leak
下面是NSOperation子类的子类实现
该操作将用于从服务器异步下载图像。
-(void) main{
@autoreleasepool {
//NSURLConnection
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@"A URl"]];
_downloadConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isExecuting{
return _isExecuting;
}
-(BOOL)isFinished{
return _isFinished;
}
-(void)finish{
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished =YES;
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
}
-(void)cancel{
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished =YES;
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[connection cancel];
connection=nil;
[self finish];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//Other Code
[connection cancel];
connection=nil;
[self finish];
}
如果我在代码中遗漏了任何内容,请告诉我,以避免泄漏并检查所有 KVO 是否已正确处理。
我发现了几个问题:
您的 finish
和 cancel
例程为每个键调用 willChangeValueForKey
两次。显然,第二次调用应该是 didChangeValueForKey
.
我建议不要实施 cancel
方法。默认实现做了一些其他的事情。不要实施该方法。如果你真的想要,我会在这里建议一些改变(至少,也取消连接;调用 super
;还有一些其他的事情),但我只是建议不要实施它并且在 didReceiveData
中检测取消(参见第 5 点)。
当操作开始时,这段代码似乎没有设置 _isExecuting
(也没有适当的 KVO)。也许这是您忘记与我们分享的 start
方法?
同样,start
方法应该检查操作是否已经被取消,如果是,立即停止操作。
在didReceiveData
中,您是否也检查isCancelled
?使操作可取消是您使用操作队列的主要原因之一。
您正在操作中启动 NSURLConnection
(可能是为了将此操作添加到某个随机队列)。但是 NSURLConnection
将无法正常工作,除非你将它安排在主 运行 循环(简单的解决方案)或者你创建自己的 运行 循环(有多种技术).
例如,要告诉操作在主 运行 循环中安排连接,您可以这样做:
_downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:FALSE];
[_downloadConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] runLoopModes:NSRunLoopCommonModes];
[_downloadConnection start];
我输入的内容没有 Xcode 的好处,所以如果我输入错误的方法请原谅我,但它说明了这个想法:与 FALSE
的 startImmediately
建立连接,安排它在主 运行 循环中,然后你才应该 start
它。
如果调用了connectionDidFinishLoading
,完全没有必要调用[connection cancel]
.
自 iOS 7 起,isConcurrent
已被弃用,取而代之的是 isAsynchronous
。但是,如果您需要支持早期的 iOS 版本,请保留 isConcurrent
.
顺便说一句,虽然我认为它可能会按照您的布局方式工作,但通常建议实现名为 executing
和 finished
的属性:
@property (nonatomic, readwrite, getter=isExecuting) BOOL executing;
@property (nonatomic, readwrite, getter=isFinished) BOOL finished;
然后我手动合成:
@synthesize finished = _finished;
@synthesize executing = _executing;
然后我实现手动设置器(但依赖合成的 getter 和 ivar):
- (void)setExecuting:(BOOL)executing
{
if (_executing != executing) {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
}
- (void)setFinished:(BOOL)finished
{
if (_finished != finished) {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
}
但是如果你这样做,你现在可以设置 self.executing = FALSE
(或其他)并且它 (a) 做适当的 KVO,避免你的代码被各种 KVO 调用弄得乱七八糟;和 (b) 但让您不必手动实现属性和吸气剂。
下面是NSOperation子类的子类实现 该操作将用于从服务器异步下载图像。
-(void) main{
@autoreleasepool {
//NSURLConnection
NSURLRequest *request=[NSURLRequest requestWithURL:[NSURL URLWithString:@"A URl"]];
_downloadConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
}
-(BOOL)isConcurrent{
return YES;
}
-(BOOL)isExecuting{
return _isExecuting;
}
-(BOOL)isFinished{
return _isFinished;
}
-(void)finish{
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished =YES;
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
}
-(void)cancel{
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished =YES;
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
[connection cancel];
connection=nil;
[self finish];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
//Other Code
[connection cancel];
connection=nil;
[self finish];
}
如果我在代码中遗漏了任何内容,请告诉我,以避免泄漏并检查所有 KVO 是否已正确处理。
我发现了几个问题:
您的
finish
和cancel
例程为每个键调用willChangeValueForKey
两次。显然,第二次调用应该是didChangeValueForKey
.我建议不要实施
cancel
方法。默认实现做了一些其他的事情。不要实施该方法。如果你真的想要,我会在这里建议一些改变(至少,也取消连接;调用super
;还有一些其他的事情),但我只是建议不要实施它并且在didReceiveData
中检测取消(参见第 5 点)。当操作开始时,这段代码似乎没有设置
_isExecuting
(也没有适当的 KVO)。也许这是您忘记与我们分享的start
方法?同样,
start
方法应该检查操作是否已经被取消,如果是,立即停止操作。在
didReceiveData
中,您是否也检查isCancelled
?使操作可取消是您使用操作队列的主要原因之一。您正在操作中启动
NSURLConnection
(可能是为了将此操作添加到某个随机队列)。但是NSURLConnection
将无法正常工作,除非你将它安排在主 运行 循环(简单的解决方案)或者你创建自己的 运行 循环(有多种技术).例如,要告诉操作在主 运行 循环中安排连接,您可以这样做:
_downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:FALSE]; [_downloadConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] runLoopModes:NSRunLoopCommonModes]; [_downloadConnection start];
我输入的内容没有 Xcode 的好处,所以如果我输入错误的方法请原谅我,但它说明了这个想法:与
FALSE
的startImmediately
建立连接,安排它在主 运行 循环中,然后你才应该start
它。如果调用了
connectionDidFinishLoading
,完全没有必要调用[connection cancel]
.自 iOS 7 起,
isConcurrent
已被弃用,取而代之的是isAsynchronous
。但是,如果您需要支持早期的 iOS 版本,请保留isConcurrent
.顺便说一句,虽然我认为它可能会按照您的布局方式工作,但通常建议实现名为
executing
和finished
的属性:@property (nonatomic, readwrite, getter=isExecuting) BOOL executing; @property (nonatomic, readwrite, getter=isFinished) BOOL finished;
然后我手动合成:
@synthesize finished = _finished; @synthesize executing = _executing;
然后我实现手动设置器(但依赖合成的 getter 和 ivar):
- (void)setExecuting:(BOOL)executing { if (_executing != executing) { [self willChangeValueForKey:@"isExecuting"]; _executing = executing; [self didChangeValueForKey:@"isExecuting"]; } } - (void)setFinished:(BOOL)finished { if (_finished != finished) { [self willChangeValueForKey:@"isFinished"]; _finished = finished; [self didChangeValueForKey:@"isFinished"]; } }
但是如果你这样做,你现在可以设置
self.executing = FALSE
(或其他)并且它 (a) 做适当的 KVO,避免你的代码被各种 KVO 调用弄得乱七八糟;和 (b) 但让您不必手动实现属性和吸气剂。