2017-03-04 4 views
2

Ich benutze NSOperation, um das Bild jeder Zelle herunterzuladen, so dass ich den Benutzer nicht mit starkem Bildladen überlasten muss. Das funktioniert 99% der Zeit, aber von Zeit zu Zeit bekomme ich eine Null in meinem Block und den App-Crash.NSOperation zum Laden eines Bildes in eine Zelle - Absturz

Dies ist der Code:

cell.blockImage.image = nil 
    cell.queue.cancelAllOperations() 

    let cacheKey = indexPath.row 

    if(self.imagesDictionary.object(forKey: cacheKey) != nil) 
    { 
     cell.blockImage.image = self.imagesDictionary.object(forKey: cacheKey) as? UIImage 
    } 
    else 
    { 
     let operation: BlockOperation = BlockOperation() 
     operation.addExecutionBlock({ 

      if let url = NSURL(string: self.arrJSONData[indexPath.row].image) { 
       if let data = NSData(contentsOf: url as URL) { 
        if let image: UIImage = UIImage(data: data as Data) 
        { 
         self.imagesDictionary.setObject(image, forKey: cacheKey as NSCopying) 
         DispatchQueue.main.async(execute: { 

          if(operation.isCancelled) 
          { 
           return 
          } 

          cell.blockImage.image = image 
         }) 
        } 
       } 
      } 



     }) 

     cell.queue.addOperation(operation) 

    } 

So downloaden i de Bild und speichert sie in einem Wörterbuch. Der Schlüssel ist der indexPath.row der Zelle.

Ich überprüfe auch, ob das Bild bereits im Wörterbuch ist, also lade ich es nicht noch einmal herunter.

Und ich setze immer mein Bild auf Null und annulliere die BlockOperation, wenn ich anfange, die Zelle zu laden.

Der Fehler, den ich immer bekommen, ist dies:

malloc: *** error for object 0x608000244c50: Invalid pointer dequeued from free list 
*** set a breakpoint in malloc_error_break to debug 

und ich habe es immer in diesem Block erhalten (aber die Linie ist immer zufällig)

if let image: UIImage = UIImage(data: data as Data) 
{ 
    self.imagesDictionary.setObject(image, forKey: cacheKey as NSCopying) 
    DispatchQueue.main.async(execute: { 

     if(operation.isCancelled) 
     { 
      return 
     } 

     cell.blockImage.image = image 
    }) 
} 

Was mache ich falsch? Vielen Dank.

+1

Ich weiß nicht, warum Sie abstürzen, aber Sie sollten mit so etwas wie https://github.com/Alamofire/AlamofireImage betrachten – EricD

+0

Ich bin kein Fan von externen Bibliotheken, aber ich sollte diese Bibliothek studieren, was tut. Vielen Dank! – Gusfat

Antwort

1

Ein paar Fragen:

  1. Wenn Sie NSMutableDictionary verwenden, müssen Sie mit diesem Wörterbuch alle Interaktionen synchronisieren. NSMutableDictionary ist nicht threadsicher. Wenn Sie NSCache verwenden, das über eine sehr ähnliche Schnittstelle verfügt, bietet es thread-sichere Interaktion, sodass keine manuelle Synchronisierung erforderlich ist.

  2. Auch sollten Sie nicht nur cell innerhalb der Operation aktualisieren. Sie wissen nicht, ob die diesem Indexpfad zugeordnete Zelle immer noch sichtbar ist (oder, noch schlimmer, ob sie für einen anderen Indexpfad wiederverwendet wurde). Sie sollten cellForRow(at:) verwenden (nicht zu verwechseln mit der ähnlich benannten UITableViewDataSource Methode), um die aktuelle Zelle zu erhalten, die dieser IndexPath zugeordnet ist. Wenn das eine Zellreferenz von nil zurückgibt, sollten Sie das verwenden, um das Bild der Zelle zu aktualisieren. Wenn es nil ist, gibt es keine sichtbaren UIImageView zu aktualisieren.

  3. Ohne Bezug zu Ihrem Absturz, wenn Sie schnell durch die Tabellenansicht blättern, kann Ihre Warteschlange von Netzwerkanfragen heruntergeladen werden, wobei Bilder für nicht mehr sichtbare Zellen heruntergeladen werden. Wenn Sie z. B. schnell zur 100. Zeile blättern, können die Anforderungen für sichtbare Zellen hinter den Anforderungen für die ersten 99 Zeilen zurückbleiben, die nicht mehr sichtbar sind. Dieses Problem wird vergrößert, wenn sie eine Netzwerkverbindung mit niedriger Geschwindigkeit haben (die Sie versuchen sollten, mit dem "Netzwerk-Link-Conditioner" zu simulieren).

    Was dies noch problematischer macht ist, dass Sie eine synchrone, nicht kündbare Netzwerkanforderung verwenden. Wenn Sie, wie EricD vorgeschlagen hat, eine der Erweiterungen UIImageView für die asynchrone Bildsuche verwendet haben (z. B. AlamofireImage, Kingfisher usw.; es gibt viele da draußen), könnte dieses Problem gemildert werden.

+0

Sorry für die lange Wartezeit, ich hatte nur Zeit, heute auf dieses Problem zu schauen.Nun, ich habe Kingfisher benutzt, es hat mir eine Menge Ärger erspart. Danke, Rob! – Gusfat

Verwandte Themen