以下是NSOperation Subclass的子类实现 该操作将用于从服务器异步下载Image。
-(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是否正确处理。
答案 0 :(得分:2)
我看到了几个问题:
您的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的好处,所以如果我错误地输入了一个方法,但是它说明了这个想法:创建与startImmediately
FALSE
的连接,在主运行循环上安排它,只有这样你才能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和ivars):
- (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)但不必手动实施属性和吸气剂。