stringWithContentsOfURL:encoding:error

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
hallo zusammen

ich hab da ein kleines problem:
die strings von zwei TextFields (lat, lon) füge ich zu einer URL zusammen (url).
per safari-quellcodeanzeige hab ich nachgeprüft, dass die URL wirklich nur auf eine zahl verweist.
diese will ich jetzt auf result ausgeben.

Code:
- (IBAction)GetURL:(id)sender
{
NSString *url;
url = [NSString stringWithFormat:@"http://ws.geonames.org/srtm3?lat=%@&lng=%@", [lat stringValue], [lon stringValue]];
[result setStringValue:[NSString stringWithContentsOfURL:[NSURL URLWithString:url] encoding:NSASCIIStringEncoding error:NULL]];
}

Problem: es kommt nur quatsch (in der art 3&4ßäü). dabei habe ich alle encodings durchprobiert (es sind ja auch nur ziffern, die sollte jeder darstellen können). komischerweise entstehen mehr zeichen, als die in safari angezeigte zahl ziffern hat. was mach ich falsch ?
anscheinend kommt das problem öfters vor, hab aber im netz keine lösung gefunden. mit NSURLDownload möchte ich das eigentlich nicht machen, da ich die datei garnicht runterladen muss, sondern nur kurz in den string packen möchte (oder geht das auch mit NSURLDownload?)

achja: nachdem ich mich durch das pdf "become an Xcoder" und auch durch http://www.macentwicklerwelt.net/doku.php?id=wiki:objective-c_fuer_cpp_-_programmierer gearbeitet habe, gibts noch einige grundlegende fragen:
1) wird nicht objective-c dadurch, dass es viel mehr als c++ zur laufzeit macht (z.B. die möglichen methoden für ein typlos definiertes objekt zu suchen oder auch das schauen fürs autoreleasen, ob keine referenz mehr auf ein objekt besteht) relativ langsamer ?
2) was genau bedeutet im interface-builder dieses ctrl-ziehen (also connections herstellen) auf klassenebene ? sind die connections im quelltext abgelegt oder im .nib-file ? warum sind sie überhaupt nötig (z.B. eine Instanz eines NSTextField ist erstellt worden, dann sollte doch auch eine andere klasse methoden davon ausführen können, ohne dass man extra connections herstellt ...?) ... vielleicht sollte ich auch noch mehr über cocoa lesen :eek:

danke schonmal für eure antworten.
gruß, phi
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Schau dir mal die Zwischenergebnisse, insbesondere den zusammen gesetzten String und die URL-Instanz an. Ist da alles okay?

Ansonsten arbeite ich ziemlich viel – vor allem bei rapid Prototyping – mit -stringWithContentsOfURL:…:…: und hatte noch keine Probleme. Aber ich mag auch Glück gehabt haben.

1.1.) Nein, ein Message-Dispatch benötigt tatsächlichen Untersuchungen zufolge etwa das 1,5- bis 2-fache eines C-Calls. Unter anderem werden die Aufrufe auch der Superklassen (!) gehasht. Jérôme hat dazu dankenswerterweise einen Artikel auf meiner Seite gepostet:
http://www.cocoading.de/Tutorials/Lauf, Forest, lauf!.html
Es gibt noch weitere Tricks, wie etwa IMPs. Außerdem ist es häufig günstiger, statt einer Iteration ein -makeObjetcsPerfromSelector:… (NSArray) zu verwenden.

1.2.) Die Speicherverwaltung sucht gar nichts zusammen, sondern benutzt Reference Counting. Hier hast du dazu einen Artikel von mir
http://www.macentwicklerwelt.net/doku.php?id=wiki:speicherverwaltung&s=speicherverwaltung

In
Rodewig/Negm-Awad, Objective-C und Cocoa, 2. Auflage
schreibe ich dazu gut 40 Seiten.

2.) Das Ziehen bedeutet, dass beim Laden des NIB-Files die entsprechende Instanzvariable gesetzt wird. Dies erfolgt vom NIB-Loader, führt also nicht zu diesen eklig Erweiterungen im Code. In Jérômes Artikel kannst du in etwa abschätzen, woher der NIB-Loader weiß, welche INstanzvariable er setzen muss.

Du scheinst noch etwas in C++ verhaftet.
 
Zuletzt bearbeitet:

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
hey

danke für deine antwort.
es muss irgendwie an der erzeugten URL-Instanz liegen.
per debugger ist urlstr noch völlig korrekt, url zeigt unter "Summary" (im debugwindow) auch den string von urlstr an, nur ist dort die variable _urlString "invalid"... irgendwas scheint er nicht zu mögen. ich hab den pfad auch mal direkt eingegeben
Code:
url2 = [NSURL URLWithString:@"http://ws.geonames.org/srtm3?lat=9&lng=30"];
mit gleichem ergebnis ... auch diesmal ist _urlString von url2 invalid ...
theoretisch sollte dort doch nochmal die angepeilte URL stehen ?!
kann es sein, dass er die sonderzeichen ?, =, & im string nicht erkennt ? aber safari schluckts doch auch ?

wegen dem rest: das waren ein paar vorschnell gestellte fragen, die sich wohl selbst beantworten, wenn ich mich einige zeit mit obj-c beschäftige :) auf jedenfall danke für die referenzen.

gruß,
phi
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Also, auf den ersten Blick kann ich keinen Fehler erkennen. Ich werde das dann morgen mal ohne TOMaten auf den Augen noch einmal versuchen.
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
danke für deine antwort.
es muss irgendwie an der erzeugten URL-Instanz liegen.
per debugger ist urlstr noch völlig korrekt, url zeigt unter "Summary" (im debugwindow) auch den string von urlstr an, nur ist dort die variable _urlString "invalid"... irgendwas scheint er nicht zu mögen. ich hab den pfad auch mal direkt eingegeben
Code:
url2 = [NSURL URLWithString:@"http://ws.geonames.org/srtm3?lat=9&lng=30"];
mit gleichem ergebnis ... auch diesmal ist _urlString von url2 invalid ...
theoretisch sollte dort doch nochmal die angepeilte URL stehen ?!
Weiß nicht, könnte lazy implementiert sein. Mach mal einen Ctrl-Klick auf das Objekt im Variablen-View und wähle "print description" oder mache gleich selbst einen NSLog( @"url instance %@", url2 );
kann es sein, dass er die sonderzeichen ?, =, & im string nicht erkennt ? aber safari schluckts doch auch ?
Eigentlich nicht, weil ich das schon verwendet habe.
wegen dem rest: das waren ein paar vorschnell gestellte fragen, die sich wohl selbst beantworten, wenn ich mich einige zeit mit obj-c beschäftige :) auf jedenfall danke für die referenzen.
Na ja, so geht es allen am Anfang, einschließlich mir. Man hat ja in den meisten Fällen vorher bestenfalls was auf Simula basierendes gemacht und muss sich konzeptionell ganz schön umstellen. :(

Aber generell gilt:
1. Mehr Abstraktion fordert mehr Indirektion. Mehr Indirektion erfordert mehr Rechenkraft.
2. Mehr Abstraktion erzeugt aber auch mehr algorithmische Optimierungsmöglichkeit.

Für mich das Wichtigste: Die knappste (und mit Abstand teuerste) Ressource sind weder CPU noch RAM, sondern Entwicklerzeit. Und wenn ich schnell ein funktionierendes Programm habe, kann ich bei Laufzeitproblemen viel Zeit darauf verwenden, "algorithmisch" die Sache zu verbessern. Eine "atomare" Sicht der Dinge verschleiert da eher.

Das kann man im generell einfach nicht beurteilen.
 

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
auf den code
Code:
NSLog(@"url instance: %@, %@", url, url2);
antwortet er mit

2007-08-06 13:30:13.065 hprofil[6673] url instance: http://ws.geonames.org/srtm3?lat=9&lng=30, http://ws.geonames.org/srtm3?lat=9&lng=30

d.h. automatisch generierte URL-instanz (url) und von hand eingefüllte (url2) bringen dasselbe ergebnis...

danke und verwirrter gruß,
phi
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Hmm, die URL scheint okay zu sein. Ja, du hast es schon 71236 Mal durchprobiert, ednnoch: Kopiere mal die URL aus dem Log-Fenster und trage sie in Safari ein. Funktioniert das?

Wenn ja, dann können wir uns ziemlich sicher sein, dass die URL-Erzeugung okay ist.

Ich hab's gerade selber gemacht und es funktionierte.

Dann schau dir noch einmal den String an. Außerdem wertest du den Error nicht aus. Wieso?
 

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
du hast ja recht, errors sollte man auch bei so kleinen testprogrämmchen abfragen... vor allem wenn sie nicht so funktionieren, wie sie sollten ;)
das stringWithContentsOfURL meldet aber (leider) keinen error.
wieso lässt mich eigentlich der debugger nicht manuell den wert von _urlString ändern ?
meine idee wäre, dort probeweise die url mit gewalt reinzuhauen und dann mal zu schauen
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Du solltest eigentlich im Kontextmenü "edit value" oder so ähnlich haben.

Also, bald bin ich soweit und hole mir das eben selbst.
 

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
ja, das hatte ich schon gefunden. was ich gemeint hab, ist, wenn ich für _urlString die adresse von urlstr angebe (sollten ja beides NSString * sein), meldet er einen bad_access ...
ich werds dann wohl doch über NSURLDownload versuchen müssen und die erzeugte datei kurz danach wieder löschen. unschön daran ist eben, dass ich das ungefähr 400mal machen muss (die arme platte :()

danke und gruß,
phi

EDIT: mit NSURLDownload funktionierts. im erzeugten file steht genau das, was safari anzeigt, ne nummer. ich tipp drauf, dass stringWithContentsOfURL einfach n bisschen "unausgereift" ist.
hat jemand eine idee, wie man mit NSURLDownload das file nicht wirklich auf der platte speichern, sondern den inhalt direkt einlesen kann ?
 
Zuletzt bearbeitet:

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Ich glaube ehrlich gesagt, dass es irgendwo ganz anders klemmt. Hast du noch etwas Zeit?
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Das wird immer mysteriöser. Okay, gerade ist die Seite offenkundig unten. Davon abgesehen hängt er bei mir schon, wenn ich den String erzeuge. Aber nur im Debugger.

?-)
 

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
zeit habe ich immer ;)
ja, die seite war kurz down, scheint aber wieder zu funktionieren. ich hab es mittlerweile aufgegeben, mir sind die ideen ausgegangen, warum es nicht funktionieren könnte :(
die andere möglichkeit wäre wohl, die daten nur in den cache zu laden, anstatt mit NSURLDownload gleich ein file auf der platte zu erzeugen. dazu muss ich mich aber noch durch die dokumentationen wühlen
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
Ich muss mir das auch noch einmal anschauen, schon für di nächste Auflage. Ich weiß nur nicht, wann. Ich versuche aber daran zu denken, dir Bescheid zu geben.

Wenn du es manuell machen möchtest kann ich nur wiederum auf NSURLRequest verweisen. Eine Übersicht mit Beispielcode gibt es hier:
Doku zum "Ladesystem"
 

pybe

Granny Smith
Registriert
02.08.07
Beiträge
16
genau, die seite hatte ich auch schon gefunden.
aber NSURLRequest generiert ja nur den request, das holen der daten funktioniert über NSURLConnection...
auf jedenfall liefert das hier das richtige ergebnis (für alle, die vielleicht mal über das gleiche prob stolpern). nachzulesen in der referenz von amin im vorigen post.
Code:
- (IBAction)createRequest:(id)sender
{
  NSString *urlstr;
  NSURL *url;

  urlstr = [NSString stringWithFormat:@"http://ws.geonames.org/srtm3?lat=%@&lng=%@", [lat stringValue], [lon stringValue]];
  url = [NSURL URLWithString:urlstr];
  NSLog(@"NSURL instance: %@", url);

  NSURLRequest *theRequest = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];

  NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
  if(theConnection) {
    receivedData=[[NSMutableData data] retain];
  }
  else NSLog(@"Keine NSURLConnection");	
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{ ... }

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{ ... }

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{ ... }

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
  NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
  [connection release];

  NSString *hdata = [NSString stringWithCString:[receivedData mutableBytes] encoding:NSASCIIStringEncoding];
  int height = [hdata intValue];
  [result setIntValue:height];

  [receivedData release];
}

vielen dank für deine hilfe.
phi
 

Amin Negm-Awad

Süsser Pfaffenapfel
Registriert
01.03.07
Beiträge
665
genau, die seite hatte ich auch schon gefunden.
aber NSURLRequest generiert ja nur den request, das holen der daten funktioniert über NSURLConnection...
Jepp, ich zitiere meist nur eine Klasse, daher auch: "NSURLRequet et al.". Der erste Weg geht dann ja ohnehin über die Themenartikel, die dann alles andere verlinken.

Trotzdem möchte ich wissen, was da falsch läuft. Ganz komische Sache. Ich fürchte bloß, dass ich Zeit investieren muss. :(
 

Peter Maurer

Pommerscher Krummstiel
Registriert
16.03.04
Beiträge
3.077
Mal 'ne Zwischenfrage: Koennte das das altbekannte ZIP-Problem sein, dass Dir, Amin, sicher was sagt? Das hat mich naemlich auch schon oefter davon abgehalten, -stringWithContentsOfURL: zu verwenden.
 

Peter Maurer

Pommerscher Krummstiel
Registriert
16.03.04
Beiträge
3.077
Ehrlich gesagt, sagt es mir nichts. Ich bin ganz Ohr!
Ich bin nicht so der Server-Fachmann, deshalb bitte ich im Voraus, meine laienhafte Ausdrucksweise zu entschuldigen:

Viele Webserver neigen ja dazu, ihre Dokumente gezipt zur Verfuegung zu stellen, um Bandbreite zu sparen. Und sie tun das immer dann, wenn der Empfaenger (hier: unser Cocoa-Programm) glaubhaft versichert, er koenne damit was anfangen.

Die -stringWithContentsOfURL:-Methode war da zumindest zu der Zeit, als ich es zuletzt ueberprueft habe, dummerweise genau dazwischen: Sie (bzw. die von ihr ausgeloeste Anfrage) hat dem Server zwar mitgeteilt, sie sei mit gezipten Daten einverstanden; hat dann aber dummerweise darauf verzichtet, die so geschickten Daten wieder zu entpacken, bevor sie sie an das Programm als NSString weiterreicht.

Mein Loesungsansatz war, die synchronen Anfragen fuer meine Beduerfnisse schlicht nachzubauen, hier als NSURL-Kategorie und kenntlich gemacht durch den "...FromNSURLConnection"-Zusatz:

Code:
@implementation NSURL (PMExtensions)

	- (NSData*)dataFromNSURLConnectionWithReturnedURL: (NSURL**)theURLPointer {
		NSMutableURLRequest *theURLRequest = [NSMutableURLRequest requestWithURL: self];
		if (theURLRequest) {
			[theURLRequest setCachePolicy: NSURLRequestReloadIgnoringCacheData];
			[theURLRequest setTimeoutInterval: 5.0];
			NSURLResponse *theResponse = nil;
			NSData *theData = [NSURLConnection sendSynchronousRequest: theURLRequest returningResponse: &theResponse error: nil];
			if (theURLPointer) {
				(*theURLPointer) = [theResponse URL];
			}
			return theData;
		}
		return nil;
	}

	- (NSData*)dataFromNSURLConnection {
		return [self dataFromNSURLConnectionWithReturnedURL: NULL];
	}

	- (NSString*)stringFromNSURLConnection {
		NSData *theData = [self dataFromNSURLConnection];
		if (theData) {
			return [[[NSString alloc] initWithData: theData encoding: NSISOLatin1StringEncoding] autorelease];
		}
		return nil;
	}

	- (NSDictionary*)dictionaryFromNSURLConnection {
		NSData *theData = [self dataFromNSURLConnection];
		if (theData) {
			return [(NSDictionary*)CFPropertyListCreateFromXMLData(kCFAllocatorDefault, (CFDataRef)theData, kCFPropertyListImmutable, NULL) autorelease];
		}
		return nil;
	}

@end
Ach ja, und fuer die o.g. Zwecke war es voellig ausreichend, NSISOLatin1StringEncoding als Kodierung fest zu verdrahten. Das kann man bei Bedarf natuerlich beliebig aufbohren.