1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Cocoa: NSString Objekt gibt sich selber frei??

Dieses Thema im Forum "OS X-Developer" wurde erstellt von Don_Camillo, 05.02.10.

  1. Don_Camillo

    Don_Camillo Golden Delicious

    Dabei seit:
    04.02.10
    Beiträge:
    8
    Hallo Forumgemeinde

    Ich wende mich an euch, da ich mit meinem Latein am Ende bin und hoffe ihr könnt mir mit meinem Problem helfen oder einen Hinweis liefern.

    Ich beschreibe euch kurz was ich machen will und dann was nicht geht. Also folgendes:
    Ich möchte eine Navigation mittels NSTableView aufbauen. Da es vorkommen kann, dass dieses Menü relativ gross ist, habe ich zwei Objekt-Klassen (Menu.m und Content.m) die mir dabei helfen sollen dieses Menü aufzubauen und ineinander zu verschachteln. Zuerst erstelle ich ein Menu-Objekt. Dieses enthält nur den Titel des Menüs und ein Array in welches die Content-Objekte hinein kommen. Die Content-Objekte enthalten dann alle weiteren Angaben für jede einzelne Zeile, wie zum Beispiel den Titel einer Zelle.

    Hier seht ihr wie ich mein Content-Objekt mit all seinen Variablen initialisiere:
    Code:
    - (id)init {
    	if (self = [super init])
        {
    		subMenu = YES;
    		imageOfContent = [[UIImage alloc] init];
    		descriptionOfContent = [[NSString alloc] init];
    		titleOfContent = [[NSString alloc] init];
    		subMenuOfContent = [[Menu alloc] init];
    		subViewOfContent = [[UIViewController alloc] init];
    		orderForNext = [[NSString alloc] init];
    		contentType = [[NSString alloc] init];
    		order = [[NSString alloc] init];
    		order2 = [[NSString alloc] init];
    		order3 = [[NSString alloc] init];
    		order4 = [[NSString alloc] init];
        }
        return self;
    }
    Wenn das Menü-Objekt mit all seinen Daten erstellt wurde lade ich den TableViewController in einen TabBarController:
    Code:
    // mainView dem TabBar Menü hinzufügen
    		mainViewController *main = [[mainViewController alloc] initWithTabBar];
    		localNavigationController = [[UINavigationController alloc] initWithRootViewController:main];
    		localNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
    		[localControllersArray addObject:localNavigationController];
    		[localNavigationController release];
    		[main release];
    
    Der mainViewController wird zuerst initialisiert und dann wird die Methode viewDidLoad aufgerufen. Und da mache ich folgendes:
    Code:
    - (void)viewDidLoad {
    	[super viewDidLoad];
     
    	tableList = [[NSMutableArray alloc] init];
    
    	mainMenu = [[Store sharedInstance] getMenu];
    
    	for(int i=0 ; i<[mainMenu numberOfMenuContents] ; i++) {
    		[tableList addObject:[[[mainMenu getMenuContents] objectAtIndex:i] getTitleOfContent]];
            }
    }
    
    Ich hole mir das Menü-Objekt das in einem Singleton gespeichert ist. Damit fülle ich nun das Array das ich für den TableView benötige um die Zeilen aufzubauen. Wenn die Methode fertig abgearbeitet ist, erwartet der TableView nun eine Eingabe, also eine Selektion einer Zeile und dann wird die Methode didSelectRowAtIndexPath aufgerufen. Und jetzt an dieser Stelle, nach dem Laden des Views und vor dem selektieren einer Zeile, werden in meinen Content-Objekten alle Strings released. Die Strings sind danach einfach nicht mehr da und ich kann auch nicht mehr darauf zugreifen. Ich kann mir das nicht erklären, denn ich habe diese Strings nicht in einem autorelease-Pool und ich mache auch keinen eigenen release der Strings. Hat jemand von euch schon eine ähnliche Erfahrung gemacht oder kann mir vielleicht einen Hinweis liefern wo ich nach dem Fehler suchen muss?
     
  2. LittlePixel

    LittlePixel Strauwalds neue Goldparmäne

    Dabei seit:
    09.07.08
    Beiträge:
    641
  3. Don_Camillo

    Don_Camillo Golden Delicious

    Dabei seit:
    04.02.10
    Beiträge:
    8
    Hallo LittlePixel

    Da konnte man mir leider bis jetzt noch nicht weiterhelfen, deshalb dachte ich schildere mein Problem mal hier in diesem Forum. Vielleicht kann mir ja hier jemand weiterhelfen.

    Gruss
     
  4. Poljpocket

    Poljpocket Salvatico di Campascio

    Dabei seit:
    07.01.07
    Beiträge:
    432
    Du gibst uns hier deutlich zu wenig Informationen für dieses anscheinend komplexere Problem. Wenn es dir nichts ausmacht, poste doch am Besten das ganze Projekt. Eine Frage noch: Wie hast du herausgefunden, dass deine Strings weg sind?

    Gruss ppocket
     
  5. Don_Camillo

    Don_Camillo Golden Delicious

    Dabei seit:
    04.02.10
    Beiträge:
    8
    Hallo ppocket

    Da ich anfangs nur einen EXC_BAD_ACCESS Error erhielt, habe ich die NSZombie Dummy-Objekte aktiviert. So wird jedesmal wenn ein Objekt wieder freigegeben wird ein Dummy-Objekt davon erstellt auf das zwar nicht mehr zugegriffen werden kann, aber das mir Informationen liefert falls dies doch von irgendwo her versucht werden sollte. So habe ich herausgefunden dass meine Objekte gar nicht mehr existieren.
    Ich konnte leider nicht das gesamte Projekt posten, ich habe dir aber alle Files in das zip gepackt die dafür von Bedeutung sind. Ausserdem würde mein App auf deinem Rechner sowieso nicht laufen, da ich meine Daten von einem Kontroller in unserem Netzwerk hole der dir ja logischerweise nicht zu Verfügung steht. Noch kurz zu den Files:
    - Menu.m: Ist mein Menü-Objekt wie ich es geschildert hatte
    - Content.m: Mein Content-Objekt, das ebenfalls zum Menü gehört
    - Socket.m: Über diese Singleton-Klasse kommuniziere ich mit dem Kontroller
    - Store.m: Darin halte ich mein Menü-Objekt auf das von mehreren Stellen her zugegriffen wird
    - MainViewController.m: Ist der erste ViewController der aufgerufen wird (genau dort passiert es dass die Objekte freigegeben werden)
    - TableViewController.m: Dieser ViewController sollte eigentlich nach dem MainViewController aufgerufen werden, soweit kommt es aber gar nicht erst

    Mal sehen, wahrscheinlich springt einem erfahrenen Cocoa-Programmierer der Fehler viel schneller ins Auge..

    Gruss
     

    Anhänge:

    • zip 2.zip
      Dateigröße:
      18,8 KB
      Aufrufe:
      55
  6. Poljpocket

    Poljpocket Salvatico di Campascio

    Dabei seit:
    07.01.07
    Beiträge:
    432
    Dein Fehler liegt genau hier: (Auszug aus dem Implimentation-File der Klasse Content)

    Code:
    
    ...
    
    - (void)setOrderForNext:(NSString *)order {
    	orderForNext = order;
    }
    
    - (void)setDescription:(NSString *)description {
    	descriptionOfContent = description;
    }
    
    - (void)setContentType:(NSString *)type {
    	contentType = type;
    }
    
    - (void)setTitleOfContent:(NSString *)title {
    	titleOfContent = title;
    }
    
    - (void)setGroupOfContent:(int *)groupOfContent {
    	group = groupOfContent;
    }
    
    - (void)setOrderOfContent:(NSString *)orderOfContent {
    	order = orderOfContent;
    }
    
    - (void)setOrder2OfContent:(NSString *)orderOfContent {
    	order2 = orderOfContent;
    }
    
    - (void)setOrder3OfContent:(NSString *)orderOfContent {
    	order3 = orderOfContent;
    }
    
    - (void)setOrder4OfContent:(NSString *)orderOfContent {
    	order4 = orderOfContent;
    }
    
    - (void)addSubMenu:(Menu *)menu {
    	subMenuOfContent = menu;
    }
    
    ...
    
    
    Was du hier machst, verletzt das Memory Management von Cocoa in einigen Hinsichten. Du allokierst in der init-Methode Speicher für alle Strings (alloc-init). Danach, in den Setter-Methoden der Klasse, wird dieser Speicher für das alte Objekt erstmal nicht wieder freigegeben. Weiter machst du ein einfaches assign auf die Strings, welche als Argument in den Setters sind. Und genau das ist falsch.

    Das MM von Cocoa sieht nämlich vor, dass der Lebenszyklus von Objekten mit einem sog. retain-count verwaltet wird. Du musst (und da gibts keinen Umweg) den retain-count deiner Objekte kontrollieren. Mit 'retain' erhöhst du diesen count und mit 'release' verringerst du ihn. Fällt der count auf 0 runter, löscht das Environment dieses Objekt, da es nicht mehr gebraucht wird.

    Ich kann dir ganz genau zeigen, wie der Fehler entsteht:

    1) In der 'applicationDidFinishLaunching:'-Methode von myHomeAppDelegate machst du das hier:
    Code:
    NSMutableArray *menuItems = [NSKeyedUnarchiver unarchiveObjectWithFile:menuPath];
    Du erstellst ein Array, welches die menuItems enthält.

    2) In derselben Methode übergibst du dieses Array dem Store. Hier schon der erste Fehler, welcher eigentlich (nach meiner Ansicht) schon eine Warnung erzeugen sollte:
    Code:
    [[Store sharedInstance] createMenu:menuItems];
    Diese Methode verlangt aber laut Interface von Store einen Doppelpointer (**). So, wie du aber in der Methode 'createMenu' mit dem Array umgehst, darf das kein Doppelpointer sein, sondern einfach ein Pointer (*).

    3) Du verarbeitest deine Daten im Array in der Methode 'createMenu' und evtl. 'createSubMenu'. Dabei werden aber die Objekte im Array nicht 'geretaint', wie es MM verlangt. Wie oben schon gesagt, verursacht genau das deinen Fehler.

    4) Die Methode 'applicationDidFinishLaunching:' kommt irgendwann mal zu einem Ende und die Referenz zu deinem Array 'menuItems' geht verloren und damit auch dessen Inhalt, weil du Cocoa nicht mitgeteilt hast, dass du diese Objekte noch brauchst (das machst du ja bekanntlich mit dem retain-count, wie beschrieben).

    5) Deine Objekte sind bei allen weiteren Aufrufen nicht mehr vorhanden.

    Voilà!! Noch einmal zusammengefasst, deine zwei Fehler:

    - Doppelpointer.
    - In deinem Code setzt das Array den retain-count seiner Inhalte um eins höher (wahrscheinlich dann auf 1) und wenn das Ende der Methode 'applicationDidFinishLaunching:' erreicht wird, löscht das Environment das Array, weil dessen retain-count auf 0 steht und damit die Inhalte (das Array setzt bevor es gelöscht wird den retain-count von seinen Inhalten runter, dann also auf 0). Deine Strings existieren also in deinem Fall genau solange, wie das Array existiert. Und das existiert nun mal nur bis ans Ende deiner 'applicationDidFinishLaunching:'-Methode!

    Der erste Fehler ist einfach wegzubringen, beim zweiten machst du das so (jede Methode muss entsprechend aussehen):
    Code:
    
    - (void)setOrderOfContent:(NSString *)orderOfContent {
    	if (![orderOfContent isEqualToString:order]) {
    		[order release];
    		order = [orderOfContent retain];
    	}
    }
    
    
    Die erste Zeile prüft, ob das vorhandene Objekt überhaupt ersetzt werden muss.
    In der zweiten Zeile wird der retain-count vom alten Objekt um eins nach unten gesetzt. Dem Environment wird signalisiert, dass dein Content-Objekt das alte Objekt order nicht mehr braucht. Ist der retain-count damit auf 0 gefallen, wird das Objekt dann aus dem Speicher gelöscht.
    In der dritten Zeile, wird der neue Wert von order gesetzt und gleichzeitig der retain-count des neuen Werts um eins erhöht. Dem Environment wird signalisiert, dass dein Content-Objekt 'orderOfContent' noch braucht und es dieses darum nicht löschen darf.

    So, ausführliche Erklärung von mir wie noch selten/nie.

    Gruss ppocket

    PS: Die Apple-Dokumentation hilft dir beim Verstehen vom Memory Management weiter. Sonst fragst du konkret hier.
     
  7. Don_Camillo

    Don_Camillo Golden Delicious

    Dabei seit:
    04.02.10
    Beiträge:
    8
    Hey ppocket, erstmal vielen Dank dass du dir soviel Mühe gemacht hast und mir einen so ausführlichen Bericht geschrieben hast. Fettes Dankeschön!

    Eigentlich hätte ich ja das Dokument von Apple bezüglich Memory Management schon gelesen. Aber das ist wohl DER ultimative Punkt bei der Cocoa-Programmierung und muss wohl 5 oder 6 mal durchgelesen werden bis man es richtig versteht. Und genau das werde ich jetzt dieses Wochenende machen und am Montag dann mein Projekt nochmals unter die Lupe nehmen. Nochmals vielen vielen Dank!

    Gruss
    Don_Camillo
     

Diese Seite empfehlen