6 Stimmen

Unterklasse Objekt mit vorimplementierter Delegatenmethode

Ich versuche, eine Unterklasse von NSURLConnection zu erstellen, die bereits eine Delegatenmethode vorimplementiert hat.

Mein aktueller Ansatz besteht darin, einen "Proxy"-Delegaten zu verwenden, der diese Methode vorbelegt und die anderen Methoden wie folgt aufruft:

-(BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection{
if ([self.delegate respondsToSelector:@selector(connectionShouldUseCredentialStorage:)]) {
    return [self.delegate connectionShouldUseCredentialStorage:connection];
}
else{
    return NULL;
}

}

Wobei Delegat der tatsächlich benutzerdefinierte Delegat ist. Dies verursacht irgendwie ein Problem, da die Rückgabe von NULL in einigen Fällen dazu führt, dass die Aktion gestoppt wird.

Was ist der richtige Weg, dies zu tun?

Meine Klasse sollte am Ende eine vordefinierte Methode aufrufen und der Rest sollte vom Entwickler implementiert werden.

Bearbeitung: Eine weitere Ergänzung, was ist der richtige Ansatz für eine void Delegatenmethode?

Bearbeitung2: Eine weitere Anforderung ist, dass die Unterklasse wie ihr Elternteil funktionieren, aber eine Delegatenmethode vorimplementiert haben muss. Der Entwickler kann zusätzlich weitere Delegaten von NSURLConnection implementieren. Kann nicht sehen, wie das mit einem benutzerdefinierten Protokoll gemacht werden kann

9voto

Antonio E. Punkte 4381

Im C ist die Definition von NULL 0, und in Objective-C wird NO auf FALSE aliasert, was wiederum auf 0 aliasiert ist, daher ist das Rückgeben von NULL im Grunde dasselbe wie das Rückgeben von NO.

Das Problem ist, wie in der Dokumentation angegeben:

Diese Methode wird aufgerufen, bevor ein Authentifizierungsversuch unternommen wird.

Wenn Sie NO zurückgeben, konsultiert die Verbindung den Anmeldeinformationenspeicher nicht automatisch und speichert keine Anmeldeinformationen. Sie können jedoch in Ihrer connection:didReceiveAuthenticationChallenge: Methode den Anmeldeinformationenspeicher selbst konsultieren und bei Bedarf Anmeldeinformationen speichern.

Das Nichtimplementieren dieser Methode ist dasselbe wie das Zurückgeben von YES.

Statt NULL zurückzugeben, gibt es YES gemäß der Standardimplementierung zurück.

EDIT: NO ist auf (BOOL)0 aliasiert, nicht auf false, das ein echter boolescher Typ ist

Speziell die Definitionen von YES/NO befinden sich in objc.h

typedef signed char     BOOL; 
// BOOL ist explizit signiert, so dass @encode(BOOL) == "c" statt "C" ist, selbst wenn -funsigned-char verwendet wird.
#define OBJC_BOOL_DEFINED

#define YES             (BOOL)1
#define NO              (BOOL)0

EDIT: Wie unten in den Kommentaren von @AminNegm-Awad angemerkt wurde, ist meine Erklärung des NULL-Werts nur eine (wahrscheinlich zu einfache), da 0 zwar seine endgültige Auswertung ist, aber nicht sein tatsächlicher Wert.

/*
 * Typdefinitionen; enthält gemeinsame Typdefinitionen, die aufgrund von [XSI] in mehreren Headerdateien verwendet werden müssen, entfernt sie aus dem Systemraum und platziert sie im Implementierungsraum.
 */

#ifdef __cplusplus
#ifdef __GNUG__
#define __DARWIN_NULL __null
#else /* ! __GNUG__ */
#ifdef __LP64__
#define __DARWIN_NULL (0L)
#else /* !__LP64__ */
#define __DARWIN_NULL 0
#endif /* __LP64__ */
#endif /* __GNUG__ */
#else /* ! __cplusplus */
#define __DARWIN_NULL ((void *)0)
#endif /* __cplusplus */

Tatsächlich kann man in nachschlagen und feststellen, dass __DARWIN_NULL im Objekt-C-Code zu ((void *)0) ausgewertet wird (nachprüfbar durch Schreiben von __DARWIN_NULL in Xcode und Klicken bei cmd), daher aus dem Kommentar von @AminNegm-Awad:

Ein ganzzahliger Konstantenausdruck mit dem Wert 0 oder ein solcher Ausdruck, der in den Typ void * überführt wird, wird als Nullzeigerkonstante bezeichnet. Wenn eine Nullzeigerkonstante in einen Zeigertyp umgewandelt wird, ist der resultierende Zeiger, genannt Nullzeiger, garantiert ungleich einem Zeiger auf ein Objekt oder eine Funktion." Als ganze Zahl beträgt 0 (Nullzeigerkonstante). Wenn es ein Zeiger ist, ist es das Umwandeln von 0 in einen Zeiger.

In einer C++-Anwendung wird __DARWIN_NULL stattdessen zu __null, einem internen Compiler.

ZURÜCK ZUR FRAGE:

Der Proxy-Delegate-Ansatz erscheint mir als sauberer Ansatz, insbesondere wenn Sie einige der NSURLConnectionDelegate-Methoden ausblenden möchten. Der Ansatz ist mehr oder weniger derselbe für -(void)-Methoden, der Unterschied besteht darin, dass Sie nichts zurückgeben müssen, sondern einfach die delegierte Methode aufrufen müssen. Ich kann Ihnen jetzt kein vollständiges Beispiel liefern, aber heute Abend werde ich etwas posten

4voto

TylerP Punkte 9691

Was Sie tun können, ist eine NSProxy-Unterklasse zu schreiben, die respondsToSelector: implementiert. So etwas in der Art:

URLConnectionProxyDelegate.h:

#import 

@interface URLConnectionProxyDelegate : NSProxy 

- (instancetype)initWithDelegate:(id)delegate;

@end

URLConnectionProxyDelegate.m:

#import "URLConnectionProxyDelegate.h"

@implementation URLConnectionProxyDelegate
{
    __weak id _realDelegate;
}

#pragma mark - Objektlebenszyklus

- (instancetype)initWithDelegate:(id)delegate
{
    if (self) {
        _realDelegate = delegate;
    }
    return self;
}

#pragma mark - NSProxy-Überschreibungen

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
    return [(id)_realDelegate methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation
{
    invocation.target = _realDelegate;
    [invocation invoke];
}

#pragma mark - Methoden des NSObject-Protokolls

- (BOOL)respondsToSelector:(SEL)sel
{
    // Ersetzen Sie @selector(connection:didFailWithError:) durch den tatsächlichen vorimplementierten Methodenselektor
    if (sel == @selector(connection:didFailWithError:)) {
        return YES;
    }

    return [_realDelegate respondsToSelector:sel];
}

#pragma mark - NSURLConnectionDelegate-Methoden

// Da ich nicht weiß, welche Methode Ihre vorimplementierte Methode ist,
// habe ich connection:didFailWithError: als Beispiel gewählt. Ersetzen Sie dies
// durch Ihre tatsächlich vorimplementierte Methode.
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"Verbindung fehlgeschlagen: Dies wird nur aufgerufen, wenn der Proxy-Delegat verwendet wird");
}

@end

Und um diese Klasse in beispielsweise einer Ihrer ViewController-Klassen zu verwenden, können Sie etwas Ähnliches tun:

SomeViewController.m:

#import "SomeViewController.h"
#import "URLConnectionProxyDelegate.h"

@interface SomeViewController () 

@end

@implementation SomeViewController

#pragma mark - Methoden für Schaltflächenaktionen

- (IBAction)testSuccessURLWithNormalDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"http://example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Verwendung von self als Delegat
    [NSURLConnection connectionWithRequest:request delegate:self];
}

- (IBAction)testFailURLWithNormalDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"keine echte URL"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Verwendung von self als Delegat
    [NSURLConnection connectionWithRequest:request delegate:self];
}

- (IBAction)testSuccessURLWithProxyDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"http://example.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Verwendung eines Proxy-Delegaten, bei dem self der eigentliche Delegat ist
    URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self];
    [NSURLConnection connectionWithRequest:request delegate:proxy];
}

- (IBAction)testFailURLWithProxyDelegate:(id)sender
{
    NSURL *url = [NSURL URLWithString:@"keine echte URL"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    // Verwendung eines Proxy-Delegaten, bei dem self der eigentliche Delegat ist
    URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self];
    [NSURLConnection connectionWithRequest:request delegate:proxy];
}

#pragma mark - NSURLConnectionDelegate-Methoden

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"Verbindung fehlgeschlagen: Dies wird nur aufgerufen, wenn der ViewController als Delegat verwendet wird");
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
    NSLog(@"Verbindung erfolgreich: Dies wird aufgerufen, wenn der ViewController ODER der Proxy-Delegat als Delegat verwendet wird");

    return YES;
}

@end

Das Wichtige bei all dem ist, dass URLConnectionProxyDelegate respondsToSelector: überschreibt und es an sein _realDelegate-Objekt weitergibt, anstatt super aufzurufen, und es gibt auch immer YES zurück, wenn der Selektor der Selektor Ihrer "vorimplementierten" Methode ist. Dies bedeutet, dass Sie nicht einmal alle anderen Methoden im NSURLConnectionDelegate-Protokoll implementieren müssen – Sie müssen nur die "vorimplementierte" Methode implementieren.

Sie könnten sogar mehrere vorimplementierte Methoden haben. Dies ist ganz einfach, indem Sie weitere Überprüfungen für den Selektor in respondsToSelector: der Proxy-Klasse hinzufügen:

[...]

if (sel == @selector(connection:didFailWithError:)) {
    return YES;
}
if (sel == @selector(connectionShouldUseCredentialStorage:)) {
    return YES;
}

[...]

... und dann sicherstellen, dass Sie natürlich alle diese Methoden auch in der Proxy-Klasse implementieren:

[...]

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"vorimplementierte connection:didFailWithError:");
}

- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
{
    NSLog(@"vorimplementierte connectionShouldUseCredentialStorage:");

    return YES;
}

[...]

Hoffentlich ergibt das Sinn und ist Ihnen von Nutzen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X