• Apfeltalk ändert einen Teil seiner Allgemeinen Geschäftsbedingungen (AGB), das Löschen von Useraccounts betreffend.
    Näheres könnt Ihr hier nachlesen: AGB-Änderung
  • Die Bildungsoffensive hier im Forum geht weiter! Jetzt sollen Kreativität und technische Möglichkeiten einen neue Dimension erreichen. Das Thema in diesem Monat lautet - Verkehrte Welt - Hier geht es lang --> Klick

Obj-C Anfängerfrage

wosinzky

Gloster
Registriert
07.02.06
Beiträge
62
Hallo Entwickler,

so langsam werde ich mit Xcode und Objective-C warm, aber die Philosophie von Obj-C, Cocoa usw. hab ich aber NOCH nicht verstanden. Da ich aber aus der C/C++ Welt komme, habe ich ein paar Verständnissprobleme. Das Buch "Programming in Objective-C" habe ich mir schon besorgt. Aber wie es immer so ist mit diesen Bücherbeispielen, sie sind immer ideal und vereinfacht gewählt (den Leser bloss nicht verwirren). D.h. auf die eigentlichen nicht trivialen Fragen bekommt man keine Antwort!

Konstruktoren/Destruktoren (ich schreibe mal K und D, das ist kürzer :-) )
--------------------------------------------------------------------------
Wie bekannt ist, ist in C++ immer ein K und ein D vorhanden. Wenn keiner implementiert wird, wird halt Einer gestellt. In Obj-C gibts es keine K's bzw. D's wie in C++, stattdessen übernimmt die alloc/init-Kombination dieselbe Rolle des K (Also ein Standard der aber nicht verbindlich ist?). Hoffentlich hab ich's bis hierher richtig verstanden.
Darüber hinaus der werden in C++ die K's und D's immer ausgerufen. Ob man will oder nicht.

Wenn ich in C++ einen String in einer Methode benutzen möchte, dann schreibe ich zB. so:

int
Klasse::methode()
{
std::string aString = "blah";
}


Wenn ich die Methode verlasse, weiss ich das der String automatisch zerstört wird (der D wird aufgerufen).

Das gleiche in Obj-C müsste dann folgendermaßen aussehen:


- (int ) methode()
{
NSString *aString;

aString = [[NSString alloc] init];

}

Das Objekt aString wird aber beim verlassen der Methode nicht zerstört (is nur nen Zeiger auf ein Obj, ich weiss). Oder doch? Wie heisst in Obj-C der D? Wenn das Obj nicht zerstört wird, dann ist der Speicherbereich auf den der Zeiger zeigt verloren? Oder etwa nicht?

Ich hoffe ihr versteht mein Verständnissproblem. Wie lange lebt ein Objekt? Wie heissen den K's und D's? Und was hat es mit den autorelease-Pools aufsich?

Hoffentlich habe ich nicht zuviele Rechtschreibfehler gemacht :-)

Thx
Wosinzky
 
In Deinem all wäre das [aString free]; allerdings hat das mit den Retain Countern von Cocoa noch nichts zu tun.
Gruß Pepi
 
In Deinem all wäre das [aString free]; allerdings hat das mit den Retain Countern von Cocoa noch nichts zu tun.
Gruß Pepi
NSObject kennt keine -free-Methode, soviel ich weiss. Ich glaube, Du meinst [aString release]. ;)

Und das hat was mit Retain-Countern zu tun: Die -release-Methode dekrementiert naemlich den Retain-Counter und ruft, falls letzterer bei Null angekommen ist, -dealloc auf. -autorelease dekrementiert den Counter haengend, naemlich dann, wenn der aktuelle Autorelease-Pool sein Leben aushaucht und dem Objekt die vorher beantragte -release-Botschaft sozusagen zurueckschickt (siehe die von MacApple o.g. Links), und deshalb geschieht auch alles andere dann ebenso haengend.

Jedenfalls gilt: Wer selber -dealloc aufruft (ausser natuerlich [super dealloc] in der -dealloc-Methode einer Unterklasse), der macht was falsch.
 
int
Klasse::methode()
{
std::string aString = "blah";
}


Wenn ich die Methode verlasse, weiss ich das der String automatisch zerstört wird (der D wird aufgerufen).

Das gleiche in Obj-C müsste dann folgendermaßen aussehen:


- (int ) methode()
{
NSString *aString;

aString = [[NSString alloc] init];

}
Das wäre IMHO wohl die direkte Übersetzung in Objective-C/Cocoa:
- (int ) methode()
{
NSString *aString = @"bla";
// sollte das gleich sein wie ...= [NSString stringWithString: @"bla"];
}

(Zerstört wird aString nicht direkt beim Verlassen der Methode, sondern durch den Autoreleasepool. Wenn ich nicht ganz falsch liege.)
 
Moin aus dem hohen Norden,

nach den anstrengenden Christi Himmelfahrt Tagen, habe ich endlich wieder ein bisschen Zeit gefunden mich den wahren Problemen dieser Welt zu widmen ;) . Hier mein kleines Getter-Setter-Testprogramm mit dem ich so meine kleinen Verständnissprobleme habe.

Die Definition

#import <Cocoa/Cocoa.h>

@interface SetGet : NSObject {
NSString *myString;
}

- (id) init;
- (void) dealloc;
- (void) set : (NSString *) newVal;
- (NSString *) get;

@end



Die Implementation

#import "SetGet.h"

@implementation SetGet

- (id) init
{
printf("=== init ===\n");

if ((self = [super init])) // superclass may return nil
{
myString = [[NSString alloc] init];
}

return self;
};

- (void) dealloc
{
printf("=== dealloc ===\n");

[myString release];

[super dealloc];
}

- (void) set : (NSString *) newVal
{
printf("=== set ===\n");
[myString release];
myString = [newVal copy];
};

- (NSString *) get
{
printf("=== get ===\n");
return myString;
};

@end



Und einmal ausprobiert

#import "SetGet.h"
#import <Cocoa/Cocoa.h>

int main(int argc, char *argv[])
{
SetGet *myTest = [[SetGet alloc] init];

[myTest set: @"a text"];

printf("%s\n", [[myTest get] UTF8String]);

[myTest release];

return 0;
}




Die Ausgabe


[Session started]
=== init ===
=== set ===
=== get ===
Setter_Getter_Tester[313] *** _NSAutoreleaseNoPool(): Object 0x3079e0 of class NSCFData autoreleased with no pool in place - just leaking
a text
=== dealloc ===

Setter_Getter_Tester has exited with status 0.



Was will er mit seinem ARP? Ich baue doch alles ordnungsgemäss auf und ab! Oder nicht? Wo ist mein Denkfehler?

MfG
Wosinzky
 
Schnellschuss -- wer's besser weiss, der korrigiere mich bitte:

Du selber machst gar keinen Fehler, Wosinzky, aber Cocoa braucht nun mal zumindest einen aktiven Autorelease-Pool. Die -UTF8String-Methode aus NSString z.B. autoreleaset ihren Rueckgabewert, wie auch der Dokumentation zu entnehmen ist (Hervorhebung von mir):

The returned C string is automatically freed just as a returned object would be released; you should copy the C string if it needs to store it outside of the autorelease context in which the C string is created.
Also mach' mal einen Autorelease-Pool um Deine Anweisungen rum und guck, ob's dann geht. :)

Code:
int main(int argc, char *argv[])
{
	NSAutoreleasePool *mainAutoreleasePool = [[NSAutoreleasePool alloc] init];
	SetGet *myTest = [[SetGet alloc] init];
	[myTest set: @"a text"];
	printf("%s\n", [[myTest get] UTF8String]);
	[myTest release];
	[mainAutoreleasePool release];
	return 0;
}
Hoffentlich hab' ich mich nicht vertippt. Abgesehen davon hat Dein Code fuer mein Gefuehl ein paar Schoenheitsfehler, aber das Fass lassen wir jetzt mal zu. Wahrscheinlich ist Dir das eh selber klar.
 

- (void) set : (NSString *) newVal
{
printf("=== set ===\n");
[myString release];
myString = [newVal copy];
};
Dieser Setter ist sehr gefährlich, denn wenn mal newVal == myString ist, wird newVal released, bevor es kopiert wird. Das ist gar nicht gut für die Stabilität des Programms. Abhilfe: myString ein autorelease schicken.

MacApple
 
Dieser Setter ist sehr gefährlich, denn wenn mal newVal == myString ist, wird newVal released, bevor es kopiert wird. Das ist gar nicht gut für die Stabilität des Programms. Abhilfe: myString ein autorelease schicken.
Das war einer der "Schoenheitsfehler", die ich gemeint hab'. :D

Und weil Setter so ein schoenes Thema sind, hier gleich noch die Variante, die ich am liebsten verwende:

Code:
- (void)setValue: (id)newValue {
	[newValue retain];
	[myValue release];
	myValue = newValue;
}
Denn merke: NSStrings sind non-mutable. In den meisten Faellen (wenn's nicht heimlich doch ein NSMutableString ist, der spaeter noch veraendert wird), reicht es also voellig aus, denselben String, der uebergeben wird, weiterzuverwenden.

Und wenn man Objekte verwendet, die das NSCopying-Protokoll nicht unterstuetzen, dann geht copy eh nicht.

Koennte uebrigens sein, dass die NSString-copy-Methode sich tatsaechlich selbst zu einer retain-Anweisung vereinfacht. Ich erinnere mich schwach, mal sowas gelesen zu haben; aber ausprobiert hab' ich's noch nie. Und es koennte auch stringWithString o.ae. betroffen haben.

Und wenn's doch kopiert werden soll, dann eben so:

Code:
- (void)setValue: (id)newValue {
	id copiedValue = [newValue copy];
	[myValue release];
	myValue = copiedValue;
}
Es bleibt natuerlich Geschmackssache; aber manchmal ist es einfach netter, nicht allzuviel Kram sich im Autorelease-Pool ansammeln zu lassen. :)
 
Das war einer der "Schoenheitsfehler", die ich gemeint hab'. :D
Das stand aber noch nicht da, als ich meinen Beitrag geschrieben habe. ;-)

Denn merke: NSStrings sind non-mutable. In den meisten Faellen (wenn's nicht heimlich doch ein NSMutableString ist, der spaeter noch veraendert wird)
Eben weil man dem Setter auch einen NSMutableString übergeben darf, muss man sich darüber im klaren sein, was das für Konsequenzen haben kann.

Und wenn man Objekte verwendet, die das NSCopying-Protokoll nicht unterstuetzen, dann geht copy eh nicht.
Logisch.

Koennte uebrigens sein, dass die NSString-copy-Methode sich tatsaechlich selbst zu einer retain-Anweisung vereinfacht. Ich erinnere mich schwach, mal sowas gelesen zu haben; aber ausprobiert hab' ich's noch nie. Und es koennte auch stringWithString o.ae. betroffen haben.
Ja, NSString ist ja ein Class-Cluster, besteht also eigentlich aus diversen anderen Klassen, die dann jeweils optimierte Methoden haben.

Es bleibt natuerlich Geschmackssache;
Wie ich einen Setter implementiere sollte keine Geschmacksfrage sein, sondern sich am erwarteten Verhalten orientieren.

MacApple