Subclass object with pre-implemented delegation method

I am trying to create a subclass of NSURLConnection that already has one delegate method.

My current approach is to use the "proxy" delegate, which pre-populates this method, and calls other methods as follows:

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

}

If the delegate is the actual user-defined delegate. This causes a problem somehow, because returning NULL in some cases leads to a suspension of the action.

What is the right way to do this?

My class should have one preconfigured method in the end, and dev must implement the other stuff.

edit: Another addition to the correct approach for the void delegate method?

Edit2: Another requirement is that the subclass must work as its parent, but must have one delegate method previously implemented. Thus, the developer can further implement other NSURLConnection delegates. Unable to see how to do this using custom protocol

+6
source share
2 answers

What you can do is write a subclass of NSProxy that implements respondsToSelector: Something like that:

URLConnectionProxyDelegate.h:

 #import <Foundation/Foundation.h> @interface URLConnectionProxyDelegate : NSProxy <NSURLConnectionDelegate> - (instancetype)initWithDelegate:(id<NSURLConnectionDelegate>)delegate; @end 

URLConnectionProxyDelegate.m:

 #import "URLConnectionProxyDelegate.h" @implementation URLConnectionProxyDelegate { __weak id<NSURLConnectionDelegate> _realDelegate; } #pragma mark - Object Lifecycle - (instancetype)initWithDelegate:(id<NSURLConnectionDelegate>)delegate { if (self) { _realDelegate = delegate; } return self; } #pragma mark - NSProxy Overrides - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [(id)_realDelegate methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation { invocation.target = _realDelegate; [invocation invoke]; } #pragma mark - NSObject Protocol Methods - (BOOL)respondsToSelector:(SEL)sel { // replace @selector(connection:didFailWithError:) with your actual pre-implemented method selector if (sel == @selector(connection:didFailWithError:)) { return YES; } return [_realDelegate respondsToSelector:sel]; } #pragma mark - NSURLConnectionDelegate Methods // Since I don't know which method your pre-implemented method is, // I just chose connection:didFailWithError: as an example. Replace this // with your actual pre-implemented method. - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Connection failed: This gets called only when the proxy delegate is used"); } @end 

And then, to use this class, for example, in a view controller class, you can do something like this:

SomeViewController.m:

 #import "SomeViewController.h" #import "URLConnectionProxyDelegate.h" @interface SomeViewController () <NSURLConnectionDelegate> @end @implementation SomeViewController #pragma mark - Button actions - (IBAction)testSuccessURLWithNormalDelegate:(id)sender { NSURL *url = [NSURL URLWithString:@"http://example.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // Using self as the delegate [NSURLConnection connectionWithRequest:request delegate:self]; } - (IBAction)testFailURLWithNormalDelegate:(id)sender { NSURL *url = [NSURL URLWithString:@"not a real url"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // Using self as the delegate [NSURLConnection connectionWithRequest:request delegate:self]; } - (IBAction)testSuccessURLWithProxyDelegate:(id)sender { NSURL *url = [NSURL URLWithString:@"http://example.com"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // Using a proxy delegate, with self as the real delegate URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self]; [NSURLConnection connectionWithRequest:request delegate:proxy]; } - (IBAction)testFailURLWithProxyDelegate:(id)sender { NSURL *url = [NSURL URLWithString:@"not a real url"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // Using a proxy delegate, with self as the real delegate URLConnectionProxyDelegate *proxy = [[URLConnectionProxyDelegate alloc] initWithDelegate:self]; [NSURLConnection connectionWithRequest:request delegate:proxy]; } #pragma mark - NSURLConnectionDelegate Methods - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Connection failed: This gets called only when the view controller is used as the delegate"); } - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection { NSLog(@"Connection success: This gets called when the view controller OR the proxy delegate is used as the delegate"); return YES; } @end 

It is important to note that URLConnectionProxyDelegate overrides respondsToSelector: and passes it along with the _realDelegate object instead of calling super , and also always returns YES if the selector is your "pre-implemented" selection method. This means that you donโ€™t even need to implement any other methods in the NSURLConnectionDelegate protocol - you just need to implement a โ€œpre-implementedโ€ one.

You may even have several pre-implemented methods. This is easy to do by simply adding additional checks for the selector in the respondsToSelector: proxy class:

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

... and then just make sure that all of these methods are also implemented in the proxy class:

 [...] - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"pre-implemented connection:didFailWithError:"); } - (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection { NSLog(@"pre-implemented connectionShouldUseCredentialStorage:"); return YES; } [...] 

Hope this makes sense and helps you.

+3
source

In C, the definition of NULL is 0, and in Objective-C, NO is smoothed to FALSE, which is smoothed to 0, so basically returning NULL is the same as returning NO.

The problem is that according to the documentation:

This method is called before any authentication attempt is made.

If you return NO, the connection will not ask for credentials to automatically store and do not store credentials. However, in your connection: didReceiveAuthenticationChallenge: method, you can consult the credential store yourself and save the credentials yourself, as needed.

Not using this method is the same as returning YES .

Instead of returning NULL, return YES according to the default implementation

EDIT: NO has an alias (BOOL)0 , not false , which is a true boolean type

In particular, the definition of YES / NO is in objc.h

 typedef signed char BOOL; // BOOL is explicitly signed so @encode(BOOL) == "c" rather than "C" // even if -funsigned-char is used. #define OBJC_BOOL_DEFINED #define YES (BOOL)1 #define NO (BOOL)0 

EDIT: As pointed out in the comments below, @ AminNegm-Awad mine is just a (possibly more) simplification of the NULL value, since 0 is how it is finally evaluated, but it is not its real value.

 /* * Type definitions; takes common type definitions that must be used * in multiple header files due to [XSI], removes them from the system * space, and puts them in the implementation space. */ #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 */ 

In fact, by looking at <sys/_types.h> , you can find out that __DARWIN_NULL for Objective-C code is evaluated as ((void *)0) (checked by writing __DARWIN_NULL to xcode and cmd + by clicking it), thus from @ Comment by AminNegm-Awad:

An integer constant expression with a value of 0 or such a cast to type void * expression is called a null pointer constant. 55) If the null value is a pointer constant converted to a pointer type, as a result, a pointer called a null pointer is guaranteed to compare unevenly with a pointer to any object or function ". As an integral, it is 0 (null is a pointer constant). If it is a pointer, then it is a pointer other than 0.

In a C ++ application, instead of __DARWIN_NULL , __null , an internal compiler, is computed.

BACK TO QUESTION:

The mediation delegate method seems clean to me, especially if you want to hide some of the NSURLConnectionDelegate methods. This approach is more or less the same for methods -(void) , the difference is that you do not need to return anything, but simply call the delegated method. Now I cannot provide you with a complete example, but tonight I will send something

+9
source

Source: https://habr.com/ru/post/969863/


All Articles