3

ich eine Klasse Dictionary, wo die init Methode wie folgt aussieht genannt haben:eine große NSDictionary Eigenschaft auf einem Nicht-Haupt-Thread Einstellung

- (id) init{ 
    self = [super init]; 
    if (self){ 
     [self makeEmojiDictionaries]; 
    } 

    return self; 
} 
- (void)makeEmojiDictionaries{ 

    //next line triggers bad_exc_access error 
    self.englishEmojiAllDictionary = @{@"hi" : @""}; //this is a strong, atomic property of NSDictionary 
}; 

Mein Problem ist, dass der tatsächliche Emoji-Wörterbuch ist recht groß, und ich wollen alle schweren Lasten in einem Nicht-Haupt-Thread mit GCD zu tun. Jedoch, wenn ich zu der Zeile komme, wo ich self.englishEmojiAllDictionary setze, bekomme ich immer einen bad_access Fehler.

Ich bin mit GCD in der normalste Art und Weise möglich:

dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL); 
    dispatch_async(myQueue, ^{ 

     //Do long process activity 
     Dictionary *dictionary = [[Dictionary alloc] init]; 

    }); 

Gibt es bestimmte Nuancen zu GCD oder Nicht-Haupt-Thread Arbeit, die ich fehle? Jede Hilfe wird sehr geschätzt - danke!

Edit 1:

Falls Sie es möchten selbst versuchen. Ich habe eine sample project hochgeladen, die diese Ausnahme repliziert. Meine Theorie ist, dass die NSDictionary I Initialisierung ist einfach zu groß.

+0

Aus welcher Klasse stammt das Wörterbuch? – Shripada

+0

Hallo @Shripada, 'Dictionary' erbt von' NSObject' – daspianist

+1

Ihr Beispiel legt 'englishEmojiAllDictionary' nicht wirklich fest. Es ist unklar, wo du zusammenstößt. Schreibst du sonst irgendwo an 'englishEmojiAllDictionary', oder sind alle anderen Anrufer schreibgeschützt? Ist 'englishEmojiAllDictionary' öffentlich oder privat (können externe Objekte direkt oder nur durch Aufruf von Methoden auf 'Dictionary' zugreifen)? Wenn Sie nicht in einer anderen Warteschlange als "Meine Warteschlange" lesen, werden Sie Kollisionen haben, aber Sie würden erwarten, dass der Absturz auf dem Leser und nicht auf dem Schreiber liegt. Machst du etwas, um sicherzustellen, dass es keine Leser gibt, bevor "makeEmojiDictionaries" abgeschlossen wird? –

Antwort

2

Ich habe Ihre Daten von Code in eine plist Datei in Form bewegt:

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 
<plist version="1.0"> 
<dict> 
<key>arancia meccanica</key><string>⏰</string> 
<key>uno freddo</key><string></string> 
<key>un drink</key><string></string> 
... 
<key>bacio</key><string></string> 
<key>baci</key><string></string> 
</dict> 
</plist> 

(ich Ihre Daten erstellt wurde und verwendet dreimal Fund ersetzen: ", =></string>, dann ":@" =></key><string> und @" =><key>).

Dann mit Ich habe die Daten geladen:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"dictionary" 
                ofType:@"plist"] 
dictionary = [NSDictionary dictionaryWithContentsOfFile:filePath]; 

, dass das Problem behoben ist. Beachten Sie, dass Sie nie Hardcode Ihre Daten in Quellcode codieren sollten.

Der genaue Grund für den Fehler war ziemlich schwer zu lokalisieren. Das NSDictionary Literal verwendet Methode +[NSDictionary dictionaryWithObjects:forKeys:count:]. Mein Assembler-Wissen ist sehr schlecht, aber ich denke, dass vor dem Aufruf dieses Initialisierers alle Schlüssel & Werte auf den Stapel gelegt werden.

Es gibt jedoch einen Unterschied zwischen der Stack-Größe des Haupt-Threads und der Stack-Größe des Hintergrund-Threads (siehe Creating Threads in Thread Programming Guide).

Das ist der Grund, warum das Problem beim Ausführen des Codes im Hintergrundthread zu sehen ist. Wenn Sie mehr Daten hätten, würde das Problem wahrscheinlich auch im Hauptthread auftreten.

Der Unterschied zwischen Stapelgröße auf Hauptthread und Hintergrund-Thread kann auch durch den folgenden einfachen Code demonstriert werden:

- (void)makeEmojiDictionaries { 
    // allocate a lot of data on the stack 
    // (approximately the number of pointers we need for our dictionary keys & values) 
    id pointersOnStack[32500 * 2]; 

    NSLog(@"%i", sizeof(pointersOnStack)); 
} 
+0

Danke, dass Sie die 'plist' Form vorgeschlagen haben. Dies scheint für große Datensätze vielversprechend zu sein, und ich werde versuchen, es zu replizieren und zu sehen, was die Ergebnisse sind, und dann die Antwort genehmigen. Vielen Dank! – daspianist

1

Die richtigen Muster, wenn Sie langsam etwas tun müssen, sind die Arbeit privat auf einem tun Hintergrundwarteschlange und dann zurück in die Hauptwarteschlange, um die abgeschlossene Arbeit für den Rest der App verfügbar zu machen. In diesem Fall müssen Sie keine eigene Warteschlange erstellen. Sie können eine der globalen Hintergrundwarteschlangen verwenden.

#import "ViewController.h" 
#import "Dictionary.h" 

@interface ViewController() 
@property (nonatomic, strong, readonly) Dictionary *dictionary; 
@end 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    [self updateViews]; 

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ 
     Dictionary *dictionary = [[Dictionary alloc] init]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      _dictionary = dictionary; 
      [self updateViews]; 
     }); 
    }); 
} 

- (void)updateViews { 
    if (self.dictionary == nil) { 
     // show an activity indicator or something 
    } else { 
     // show UI using self.dictionary 
    } 
} 

@end 

das Wörterbuch aus einer Datei zu laden ist eine gute Idee, und Sie können, dass im Hintergrund Warteschlange tun und dann Stück zurück zu dem Haupt-Thread mit dem geladenen Wörterbuch.

+0

Dies ist zwar nützlich, aber leider nicht die Wurzel des Problems - dies wird immer noch abstürzen. – Sulthan

+0

Wie so? Ich habe das Beispielprojekt von daspianist heruntergeladen. Es ist nicht für mich abgestürzt. Diese Änderungen stürzen auch nicht für mich ab. Erklären Sie, wie Sie diesen Absturz verursachen können. –

+0

Xcode 7.3, Simulator 6s Plus, es stürzt für mich ohne Änderungen ab. – Sulthan

2

Als erstes empfehle ich Ihnen, eine Datei (plist, txt, xml, ...) zu verwenden, um große Daten zu speichern, dann zur Laufzeit zu lesen oder von einem entfernten Server herunterzuladen.

Für Ihr Problem ist es wegen der Begrenzung der Stapelgröße. Unter iOS ist die Standardstapelgröße für den Hauptthread 1 MB und 512 KB für die sekundären Threads. Sie können es über [NSThread currentThread].stackSize überprüfen.
Ihr hardcoded Wörterbuch kostet fast 1 MB Stapel, deshalb wird Ihre App auf einem sekundären Thread abstürzen, aber auf dem Hauptthread OK sein.

Wenn Sie mit einem Hintergrundthread arbeiten möchten, müssen Sie die Stackgröße für diesen Thread erhöhen.
Zum Beispiel:

// NSThread way: 
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(populateDictionaries) object:nil]; 
thread.stackSize = 1024*1024; 
[thread start]; 

Oder

// POSIX way: 
#include <pthread.h> 

static void *posixThreadFunc(void *arg) { 
    Dictionary *emojiDictionary = [[Dictionary alloc] init]; 
    return NULL; 
} 

@implementation ViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    pthread_t posixThread; 
    pthread_attr_t stackSizeAttribute; 
    size_t   stackSize = 0; 
    pthread_attr_init (&stackSizeAttribute); 
    pthread_attr_getstacksize(&stackSizeAttribute, &stackSize); 
    if (stackSize < 1024*1024) { 
     pthread_attr_setstacksize (&stackSizeAttribute, REQUIRED_STACK_SIZE); 
    } 
    pthread_create(&posixThread, &stackSizeAttribute, &posixThreadFunc, NULL); 
} 

@end 

Oder

// Create mutable dictionary to prevent stack from overflowing 
- (void)makeEmojiDictionaries { 
    NSMutableDictionary *dict = [NSMutableDictionary dictionary]; 
    dict[@"arancia meccanica"] = @"⏰"; 
    dict[@"uno freddo"] = @""; 
    dict[@"un drink"] = @""; 
    ..... 
    self.englishEmojiAllDictionary = [dict copy]; 
} 

FYI:

+0

Zuerst dachte ich, dass Sie nur meine Antwort kopieren, aber Sie fügen tatsächlich interessante Informationen hinzu. Übrigens benötigt das Wörterbuch nicht 1 MB auf dem Stack, es dauert 'number_of_items * 2 * sizeof (id)' was für 32k Zeilen ungefähr 512 kB ist. – Sulthan

+0

@Sulthan Ja, ich danke Ihnen, dass Sie darauf hinweisen. Ich meine, die zum Ausführen dieser Thread-Arbeit benötigte Stack-Größe beträgt fast 1 MB, was die reservierte Größe für lokale Variablen und einige Buchhaltungsdaten einschließt. –

+0

Wirklich hilfreiches Zeug, besonders der Vorschlag, ein 'NSMutableDictionary' zu verwenden, ist sehr interessant. Eine schnelle Frage, was das Abrufen von Informationen betrifft, wäre ein änderbares Wörterbuch genauso effizient wie ein nicht veränderbares? – daspianist

Verwandte Themen