Obj-C syntax, åtkomst av metod.

Tråden skapades och har fått 12 svar. Det senaste inlägget skrevs .
1
  • Medlem
  • Stockholm
  • 2006-01-06 14:28

Jag sitter med en uppgift ur Stepehn G. Kochans Programming in Objective C (Uppgift 8.6 för den som kanske redan har gjort uppgiften).

Jag har en klass Rectangle som har variablerna:

@interface Rectangle : GraphicObject
{
	float width;
	Rectangle *resultRect;
	Point *origin;
}

Där Point är en annan underklass till GraphicObject innehållande flyttalen x och y, vilka man kommer åt med metoderna x och y.

Från GraphicObject ärvs (säger man så?) också

float height;

Således har objektet origin (utgångspunkt(x och y)), height (höjd) och width (bredd).

Rectangle har en metod, intersect, som räknar ut huruvida två rektanglar (myRect och yourRect)överlappar varandra, och om så är fallet, utgångspunkt, höjd och bredd hos den överlappande rektangeln (resultRect ovan), vilken den returnerar.

Jag har inga problem med att räkna ut resultRect, men däremot kommer jag inte åt origin utanför min metod. I intersect kommer jag åt origins x och y enligt

[[resultRect origin] x];

Men min main-rutin kommer inte åt med

[[[myRect intersect: yourRect] origin] x]

som i likhet med y lustigt nog pekar på height, det där flyttalet som ärvdes från superklassen, ni minns.

Samtidigt har jag inga problem att nå height respektive width med t. ex.

[[myRect intersect: yourRect] height]

Snälla! Hjälp mig! Det känns som att jag har missat något grundläggande!

Vänligen, Ylan, som hoppas att han har skrivit så att det går att begripa!

När du säger "kommer inte åt", hur menar du då? Får du något felmeddelande från kompilatorn, fel i runtime eller blir värdet 0?

  • Medlem
  • Stockholm
  • 2006-01-06 15:13
Ursprungligen av morris:

När du säger "kommer inte åt", hur menar du då? Får du något felmeddelande från kompilatorn, fel i runtime eller blir värdet 0?

Jag får värdet i height!! Flyttalsvariabeln från superklassen!

Vänligen, Ylan

Du menar alltså att -y och -x från Point returnerar samma värden som -height och -width från Rectangle? Skulle väl kunna bero på att -intersect: helt enkelt skapar en ny Rectangle med felaktiga värden?

  • Medlem
  • Stockholm
  • 2006-01-07 11:26
Ursprungligen av morris:

Du menar alltså att -y och -x från Point returnerar samma värden som -height och -width från Rectangle? Skulle väl kunna bero på att -intersect: helt enkelt skapar en ny Rectangle med felaktiga värden?

Det som är så märkligt är att:

metoden

(Rectangle *) intersect (Rectangle *) yourRect

returnerar resultRect som är en instance av Rectangle, och således innehåller origin (x och y), width och height.

Följande rad i metoden intersect

printf("%g, %g\n", [[resultRect origin] x], [[resultRect origin] y]);

skriver korrekta värden på x och y.

Där följande rad i main, som kallar metoden intersect, som ju returnerar resultRect,

printf("Origin at (x = %g, y = %g )\n", [[[myRect intersect: yourRect] origin] x], [[[myRect intersect: yourRect] origin] y]);

ger felaktigt värde.

Vad kan jag ha missförstått eller var har jag helt enkelt klantat mig?

Vänligen, Ylan

Låter ju väldigt lustigt. Har du möjlighet att lägga upp sourcen nånstans? I så fall kan jag kika på den.

  • Medlem
  • Stockholm
  • 2006-01-07 23:32
Ursprungligen av morris:

Låter ju väldigt lustigt. Har du möjlighet att lägga upp sourcen nånstans? I så fall kan jag kika på den.

Juste!

Den finns på denna sida

Vänligen, Ylan

Okej, jag har hittat problemet i alla fall. Jag ska inte kommentera allt för mycket på koden, för jag har inte själv läst boken du följer så jag antar att den bara går igenom språket Objective-C och inte Cocoa då. Till exempel ser jag att du ärver från Object istället för NSObject. Men som sagt, det spelar ingen roll i det här kontextet.

Problemet jag ser ligger i Rectangle -intersect:. Du allokerar en ny Rectangle, resultRect, som en instansvariabel till den Rectangle du intersectar. Sedan accessar du dess instansvariabel origin för att sätta x/y-koordinaterna. Problemet är att det inte finns någon origin i det läget. Instansvariabeln origin skapas bara i -setOrigin:, och den används inte här. En lösning är att flytta instansieringen av origin från -setOrigin: till -init, så att det alltid finns en origin. Här är en diff som illustrerar detta (+ är nya rader och - är borttagna rader om du inte är bekant med formatet):

--- Rektangel.old/Rectangle.m   2006-01-07 23:16:21.000000000 +0100
+++ Rektangel/Rectangle.m       2006-01-08 00:43:36.000000000 +0100
@@ -6,6 +6,15 @@
 @implementation Rectangle;

 //Setters
+
+- (id)init {
+        [super init];
+
+        origin = [[Point alloc] init];
+
+        return self;
+}
+
 -(void)setWidth: (float) w
 {
        width = w;
@@ -24,8 +33,6 @@

 -(void) setOrigin: (Point *) pt
 {
-       origin = [[Point alloc] init];
-
        [origin setX: [pt x] andY: [pt y]];

 }

Anledningen att du får så konstiga värden, det vill säga värdena för width och height, är lite invecklade. Normalt i Objective-C garanteras det att om man försöker skicka ett meddelande till nil så får man nil tillbaka. Således:

NSString *string = nil;
NSString *newString = [string copy];
unsigned int length = [newString length]

Vi skapar en variabel string som är nil, försöker kopiera den till newString som då blir nil eftersom meddelanden till nil returnerar nil. Vi försöker ta längden på newString som då blir 0. Det sista är lite intressant, för standarden säger bara att vi får objektet nil tillbaka, det säger ingenting om att vi ska få intvärdet 0 tillbaka. Just för intvärden råkar det då fungera, men för flyttalsvärden, som din float, fungerar det däremot inte. Vad du istället får tillbaka är det värde som råkade ligga på stacken just då, de flyttal du använde senast, nämligen height.

Kontentan är i alla fall att du ska passa dig för att ta flyttal från objekt som kan vara nil, för du kan få vad som helst tillbaka.

Till sist kan jag nämna att jag hittade felet genom att starta programmet i debuggern, och kolla alla instansvariabler i resultRect. Då märkte jag att resultRect->origin var nil.

  • Medlem
  • Stockholm
  • 2006-01-08 01:37

Tack så mycket morris,

jag hade en tanke att det kunde vara något sådant, men då jag under felsökningen ändrade på värdet på height (både x och y pekade på eller fick värdet från height) fattade jag helt plötsligt ingenting. Det är när man gör sådana fel som detta, man verkligen lär sig något!

I synnerhet när man får sådan fantastisk hjälp!!

Vänligen, Ylan, som låter projektet ligga kvar tills vidare för eventuellt intresserade!

  • Medlem
  • Stockholm
  • 2006-01-08 01:47

Det slog mig att jag använt en liknande override när jag frigjort minne (precis som du såg har jag inte börjat med Cocoa än). Jämför:

- (id)init
{
        [super init];
	
	origin = [[Point alloc] init];
	
	return self;
}

-(id) free
{
	if(origin)
		[origin free];
	if(resultRect)
		[resultRect free];
	return [super free];
}

Men fungerar inte dessa endast så länge inte superklassen inte har egna metoder init och free?

Vänligen, Ylan

Nej, tvärtom fungerar de endast så länge superklassen har -init och -free, eftersom båda kallar på superklassens variant med [super init/free]. Just de metoderna kommer i det här fallet från rootklassen Object. Om superklassen GraphicObject hade sin egen -init hade kedjan gått [Rectangle init] -> [GraphicObject init] -> [Object init]. Du kan titta på den kedjan själv genom att skapa en initmetod i GraphicObject, och sedan lägga in printf-satser i båda klasserna.

  • Medlem
  • Stockholm
  • 2006-01-08 02:25
Ursprungligen av morris:

Nej, tvärtom fungerar de endast så länge superklassen har -init och -free, eftersom båda kallar på superklassens variant med [super init/free]. Just de metoderna kommer i det här fallet från rootklassen Object. Om superklassen GraphicObject hade sin egen -init hade kedjan gått [Rectangle init] -> [GraphicObject init] -> [Object init]. Du kan titta på den kedjan själv genom att skapa en initmetod i GraphicObject, och sedan lägga in printf-satser i båda klasserna.

OK, och så länge man är noga med att kalla vidare till superklassens superklass, riskerar man inte bryta kedjan för klassens underklasser! Jag menar om jag i GraphicObject inför en metod som heter init , men sysslar med att omvandla decimala tal till oktala, lär det ju vara föga meningsfullt att kalla metoden!

Vänligen, Ylan, som såg att han lagt in ett inte för mycket i sitt tidigare inlägg!

Huruvida man ska kolla superklassens implementation beror lite på situationen. Ibland vill man helt ta över en implementation och inte alls låta den tidigare göra något alls, och ibland vill man bara göra lite extra före eller efter metoden. Men precis som du säger är det viktigt att se till att kedan av -init och -free (eller -dealloc i Cocoa) fungerar ordentligt. Vanligtvis får man dock varningar från kompilatorn om man försöker göra något annat.

1
Bevaka tråden