NSOperation Subclass表现或泄漏

时间:2015-04-04 02:42:31

标签: ios objective-c performance nsoperation nsoperationqueue

以下是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是否正确处理。

1 个答案:

答案 0 :(得分:2)

我看到了几个问题:

  1. 您的finishcancel例程正在为每个密钥调用willChangeValueForKey两次。显然第二个电话应该是didChangeValueForKey

  2. 我建议不要实施cancel方法。默认实现做了一些其他的事情。不要实现该方法。如果你真的想要,我会在这里提出一些改变(至少也取消连接;也可以调用super;还有其他一些东西),但我只是建议不要实施它检测didReceiveData中的取消(参见第5点)。

  3. 操作开始时,此代码似乎没有设置_isExecuting(也没有适当的KVO)。也许这是你忽略了与我们分享的start方法?

  4. 同样,start方法应检查操作是否已被取消,如果是,请立即停止操作。

  5. didReceiveData,您还在检查isCancelled吗?使操作可取消是您使用操作队列的主要原因之一。

  6. 您正在操作中启动NSURLConnection(可能是为了将此操作添加到某个随机队列)。但是NSURLConnection将无法正常工作,除非您在主运行循环(简单解决方案)上安排它,或者您创建自己的运行循环(有各种技术)。

    例如,要告诉操作在主运行循环中安排连接,您可以执行以下操作:

    _downloadConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:FALSE];
    [_downloadConnection scheduleInRunLoop:[NSRunLoop mainRunLoop] runLoopModes:NSRunLoopCommonModes];
    [_downloadConnection start];
    

    我输入的内容没有Xcode的好处,所以如果我错误地输入了一个方法,但是它说明了这个想法:创建与startImmediately FALSE的连接,在主运行循环上安排它,只有这样你才能start

  7. 如果调用connectionDidFinishLoading,则完全没有必要致电[connection cancel]

  8. 自iOS 7起,isConcurrent已被弃用,转而使用isAsynchronous。但是,如果您需要支持早期的iOS版本,请保留isConcurrent

  9. 顺便说一句,虽然我认为它可能会按照你的方式运作,但通常建议实现名为executingfinished的属性:

     @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)但不必手动实施属性和吸气剂。