0

Ich versuche, das NSFastEnumeration-Protokoll für eine SQLite-Abfrage zu implementieren.NSFastEnumeration-Nachricht an freigegebene Instanz gesendet

ich in leite: Nachricht an deallokierten Instanz

gesendet
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained *)stackbuf count:(NSUInteger)len { 

    // First call 
    if(state->state == 0) { 
     state->mutationsPtr = &state->extra[0]; 
     state->state = 1; 
     sqlite3_reset(self.statement); 
    } 

    state->itemsPtr = stackbuf; 

    NSUInteger count = 0; 
    while (count < len) { 
     int result = sqlite3_step(self.statement); 

     if (result == SQLITE_DONE) { 
      break; 
     } 

     MyRow *row = [self queryRow]; 
     stackbuf[count] = row; 
     count += 1; 
    } 

    return count; 
} 

-(MyRow *) queryRow { 
    MyRow * row = // query sqlite for row 
    return row; 
} 

Es scheint, als ob das ‚Reihe‘ Objekt nicht zurückgehalten werden, so dass, wenn es braucht in Schleife sein bereits freigegeben worden zugegriffen werden.

Muss ich die Ergebnisse speichern, wenn ich in 'countByEnumeratingWithState' in einem'strong'-Datensatz iteriere, so dass es beibehalten wird?

IE:

@property (nonatomic, strong) NSMutableArray *resultList; 

dann innerhalb der while-Schleife:

while (count < len) { 
    int result = sqlite3_step(self.statement); 

    if (result == SQLITE_DONE) { 
     break; 
    } 

    MyRow *row = [self queryRow]; 
    [self.resultList addObject:row]; // throw into a strong array so its retained 
    stackbuf[count] = row; 
    count += 1; 
} 

EDIT:

Ein wenig mehr Forschung zeigt, dass vielleicht nur __autoreleasing verwende ich kann:

MyRow * __autoreleasing row = [self queryRow]; 

Ohne ein starkes Array von Objekten verwalten zu müssen. Ist das die richtige Lösung?

+1

Sie sollten Methoden nicht benennen 'erhalten ...'. Namenskonventionen haben in ARC eine Bedeutung. – Sulthan

+0

@Sultan, behoben. Immer noch bleibt die Frage ... – lostintranslation

Antwort

1

Das schnelle Aufzählungsprotokoll beruht auf der Auflistung, die es auflistet, seine enthaltenen Elemente beizubehalten. Der Aufrufer (der Compiler) stellt sicher, dass die Sammlung selbst während der Enumeration beibehalten wird.

Das von countByEnumeratingWithState: verwendete Array enthält __unsafe_unretained Referenzen. Dies ist sicher, da der Compiler die Sammlung behält, die Sammlung die Elemente beibehält und die Referenzen im Array daher gültig bleiben.

Auf der Sprachebene wird eine Objektreferenz, die von der schnellen Aufzählung zurückgegeben wird, vom Aufrufer nicht berücksichtigt und muss bei Bedarf beibehalten werden, was natürlich automatisch von ARC gehandhabt wird. Dies ist nicht anders als bei der Rückgabe von Elementen aus anderen Sammlungen (Arrays, Wörterbücher usw.).

Jetzt ist Ihre "Sammlung" anders, sie enthält keine Elemente, sondern erhält sie aus einer SQL-Abfrage bei Bedarf. Diese Elemente gehören nicht Ihrer "Sammlung" und werden daher von ARC freigegeben, wenn keine starken Verweise mehr auf sie vorhanden sind. Daher sind die __unsafe_unretained Verweise, die Sie in schnellen Aufzählungen C-Array speichern, wirklich unsicher - ARC hebt auf, was sie referenzieren.

Die Lösung besteht darin, eine Standardkollektion, z. B. NSMutableArray, zu Ihrer "Sammlung" hinzuzufügen (d. H. Instanzvariable). Bei jedem Aufruf von countByEnumeratingWithState: leeren Sie zuerst diese Auflistung und verwerfen dabei alle Verweise, die Sie auf vorherige Abfrageergebnisse haben (die sie auch dann freigeben, wenn der aufrufende Code sie nicht beibehalten hat) und füllen sie dann mit den Abfrageergebnissen, für die zurückgegeben wird dieser Anruf.

Wenn Ihre "Sammlung" selbst schließlich von ARC freigegeben wird, werden alle Verweise auf Abfrageergebnisse, die sie noch enthält, ebenfalls verworfen.

Es lohnt sich, Apple's Enumeration Sample zu lesen, da seine Kommentare Details der Speicherverwaltung enthalten, die zur Implementierung einer schnellen Aufzählung erforderlich sind.

HTH

+0

das hilft und macht Sinn. Das habe ich im Abschnitt "IE" vorgeschlagen. Ich konnte es auch ohne die Instanzvariable mit dem Flag "__autoreleasing" arbeiten lassen. Siehe EDIT. Ist das auch eine Möglichkeit oder nur Glück? – lostintranslation

+0

Ja, ich vermied es sorgfältig, "__autoreleasing" zu erwähnen, Sie haben mich erwischt ;-) Seien Sie sehr vorsichtig mit '__autoreleasing', das war für einen bestimmten Anwendungsfall gedacht (Nachschlagen ARC und' NSError ** '). Stellen Sie sicher, dass Sie verstehen, was es tut (lesen Sie die Clang ARC-Dokumentation). Ich sage nicht, dass es hier nicht funktioniert *, aber Sie müssen die Semantik selbst herausfinden, damit Sie sie wirklich verstehen, oder Sie werden wahrscheinlich am Ende in den Fuß schießen. Sie könnten zu Ihrer Frage Ihre * Argumentation * hinzufügen, warum Sie denken, "__autoreleasing" sollte funktionieren und dann sehen, ob andere es für Sie bestätigen. – CRD

Verwandte Themen