Is it possible to call the original delegate from a delegate? - objective-c

Out of curiosity, is it possible to call the original delegate method implementation in a custom delegate implementation. like [super delegateMethod]; Or is that not possible. There are some scenarios where'd id like to add customizations to the behavior if certain conditions are met. Thank you in advance!

Yes, this can be achieved via a wrapper object which intercepts messages and forwards them to another delegate object. The following class intercepts calls to the scrollViewDidScroll: method before forwarding it (and any other delegate method invocations) to another UIScrollViewDelegate.
#import UIKit;
#interface ScrollHandler : NSObject <UIScrollViewDelegate>
- (instancetype)initWithScrollViewDelegate:(id<UIScrollViewDelegate>)delegate;
#end
#implementation ScrollHandler {
id<UIScrollViewDelegate> _scrollViewDelegate;
}
- (instancetype)initWithScrollViewDelegate:(id<UIScrollViewDelegate>)delegate {
self = [super init];
if (self) {
_scrollViewDelegate = delegate;
}
return self;
}
// Intercept specific method invocations:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// Implement custom behavior here before forwarding the invocation to _scrollViewDelegate
[_scrollViewDelegate scrollViewDidScroll:scrollView];
}
// Forward unintercepted method invocations:
- (BOOL)respondsToSelector:(SEL)selector
{
// Ask _scrollViewDelegate if it responds to the selector (and ourself as well)
return [_scrollViewDelegate respondsToSelector:selector] || [super respondsToSelector:selector];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// Forward the invocation to _scrollViewDelegate
[invocation invokeWithTarget:_scrollViewDelegate];
}
#end
Example use:
_scrollView = [[ScrollHandler alloc] init];
_scrollController = [[ScrollHandler alloc] initWithScrollViewDelegate:self];
_scrollView.delegate = _scrollController;

Related

Prevent class from being subclassed in Objective-c

How do I prevent a particular class from being subclassed?
I am not aware of such functionality (say final keyword for example) in the language. However Apple says it has done so for all classes in AddressBookUI.framework (in iOS)
For educational purposes, how can I achieve the same functionality, or how would they have done such thing?
From iOS7 Release Notes(Requires login) :
Here's one way: override allocWithZone: from within your "final" class (substituting MyFinalClassName for your actual class name) like this:
+ (id)allocWithZone:(struct _NSZone *)zone
{
if (self != [MyFinalClassName class]) {
NSAssert(nil, #"Subclassing MyFinalClassName not allowed.");
return nil;
}
return [super allocWithZone:zone];
}
This will prevent a subclass that is not a member of MyFinalClassName from being alloc'ed (and therefore init'ed as well), since NSObject's allocWithZone: must be called eventually, and by refusing to call super from your "final" class, you will prevent this.
There's a simpler way to prevent subclassing in Xcode 6 as a result of Swift interop. To prevent Swift classes from being subclassed in Objective-C the objc_subclassing_restricted is added to all class definitions in the {ProjectName}-Swift.h file.
You can use this in your projects:
#if defined(__has_attribute) && __has_attribute(objc_subclassing_restricted)
# define FOO_FINAL __attribute__((objc_subclassing_restricted))
#else
# define FOO_FINAL
#endif
FOO_FINAL
#interface Foo : NSObject
#end
#interface Bar : Foo
#end
The compiler will halt on the definition of Bar with Cannot subclass a class with objc_subclassing_restricted attribute
Here is possible solution:
#interface FinalClass : NSObject
#end
#implementation FinalClass
- (id)init
{
if (self.class != [FinalClass class]) {
return nil;
}
self = [super init];
if (self) {
// instance initialization
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
#end
#implementation InvalidSubclass
- (id)init
{
self = [super init];
if (self) {
}
return self;
}
#end
I'm not sure this is 100% guaranteed because it's runtime-checking anyway, but it should be enough to block and warn people that they should not subclass this. Subclass might skip superclass's init, but then the instance will not be usable because it's not fully initialised by superclass.
Something like the following will ensure that every time an "impossible subclass" calls +alloc, an object will be allocated that is an instance of FinalClass, and not the subclass. This is essentially what NSObject's +alloc method does, but here we specify an explicit class to create. This is how NSObject allocates instances (in Obj-C 2), but there is no guarantee this will always be the case, so you may want to add an appropriate -dealloc which calls object_dispose. This method also means you don't get a nil object back if you try to instantiate a subclass - you do get an instance of FinalClass.
#interface FinalClass: NSObject
//...
+ (id)alloc; // Optional
#end
// ...
#import <objc/runtime.h>
#implementation FinalClass
+ (id)alloc {
if (![self isMemberOfClass:[FinalClass class]]) {
// Emit warning about invalid subclass being ignored.
}
self = class_createInstance([FinalClass class], 0);
if (self == nil) {
// Error handling
}
return self;
}
#end
#interface InvalidSubclass : FinalClass
// Anything not in FinalClass will not work as +alloc will
// create a FinalClass instance.
#end
Note: I'm not sure I'd use this myself - specifying that a class shouldn't be subclassed is more in the nature of a design-contract with the programmer rather than an enforced rule at compile- or runtime.

objective c message forwarding with forwardingTargetForSelector not always working

I have a view controller which defines a protocol which itself inherits another protocol.
I want any object that implements my protocol to also implement the inherited protocol.
I want to set my class to intercept some of the messages in the inherited protocol in order to configure some things internally but eventually would like to forward all of the messages to the delegate of my class
I could write a lot of boiler plate code to stub all of the protocol and intern call the delegate but I see that it breaks a lot of the time - any time the "super" protocol changes I need to restub this class once again.
I see that this is very predominant in custom UI controls. When reusing existing components - for instance tables or collection views you would like your data source to respond to all of the common protocols but some instances you need to configure the view according to the index or save a particular state.
I've tried using forwardingTargetForSelector in order to forward the messages I do not respond to , but it isn't always forwarding...
Here is a contrived code example:
Class A: (the top most protocol)
#
protocol classAProtocol <NSObject>
-(void)method1;
-(void)method2;
-(void)method3;
#end
My Class
#protocol MyClassProtocol <classAProtocol>
-(void)method4;
#end
#interface MyClass
#property (nonatomic,weak> id <MyClassProtocol> delegate;
#end
#interface MyClass (privateInterface)
#property (nonatomic,strong) ClassA *classAObject;
#end
#implementation MyClass
-(init)
{
self = [super init];
if (self)
{
_classAObject = [[ClassA alloc] init];
_classAObject.delegate = self; // want to answer some of the delegate methods but not all
}
}
-(void)method1
{
// do some internal configuration
// call my delegate with
[self.delegate method1];
}
-(id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self respondsToSelector:aSelector])
{
return self;
}
if ([self.delegate respondsToSelector:aSelector])
{
return self.delegate;
}
return nil;
}
-(void)setDelegate:(id <MyClassProtocol>)delegate
{
self.delegate = delegate; // will forward some of the messages
}
#end
Returning self from forwardingTargetForSelector: makes no sense because it would never be called if self responded to the selector.
You need to implement these three methods:
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([self.delegate respondsToSelector:[anInvocation selector]])
[anInvocation invokeWithTarget:self.delegate];
else
[super forwardInvocation:anInvocation];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [super respondsToSelector:aSelector] || [self.delegate respondsToSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [self.delegate methodSignatureForSelector:selector];
}
return signature;
}
You shouldn't ever return self; from forwardingTargetForSelector:. Your check should mean it never is but if you ever did return self it would cause an infinite loop.
You need to be sure that a super class isn't implementing the method as this will prevent forwardingTargetForSelector: from being called. Check if the method is actually called.
forwardingTargetForSelector: is also only called when a method is called on your controller that it doesn't respond to. In your example you aren't calling [self ...];, you're calling [self.delegate ...]; so forwardingTargetForSelector: will not be called.

OCMock: Mocking protocols with excluding optional methods

I'm using OCMock for creating mocks in my tests for my iOS app, and I'd like to create mocks of protocols that don't implement all of the optional methods.
If it's not clear what I mean... here's some code:
// Protocol definition
#protocol MyAwesomeProtocol
- (void)doThatRequiredThing;
#optional
- (void)doThatOptionalThing;
#end
...
// In a test
id mock = [OCMockObject mockObjectForProtocol:#protocol(MyAwesomeProtocol)];
// This should return YES:
[mock respondsToSelector:#selector(doThatRequiredThing)];
// This should return NO:
[mock respondsToSelector:#selector(doThatOptionalThing)];
I hit this limitation as well. The basic idea is to override respondsToSelector: (which CANNOT be reliably mocked by OCMock).
I made the following class which does this for you.
You can then use it as follows:
extend GCOCMockOptionalMethodSupportingObject, and implement your protocol
#interface GCTestDelegate : GCOCMockOptionalMethodSupportingObject <GCDelegate>
#end
#implementation GCTestDelegate
//required methods
- (void)requiredMethod{
}
#end
// create your testdelegate
self.classBeingTested.delegate = [OCMock partialMockForObject:[GCTestDelegate new]];
[self.classBeingTested.delegate markSelectorAsImplemented:#selector(optionalMethod:)];
[[self.classBeingTested.delegate expect] optionalMethod:self.classBeingTested];
[self.classBeingTested doSomethingThatwillCheckIfYourDelegateRespondsToYourOptionalMethod];
If you do not call markSelectorAsImplemented, then your classBeingTested will get NO for respondsToSleectorForThatMethod
I've put the code for it here. I'm using this to great effect. Thanks to jer on #iphonedev for setting me off on this path (overriding respondsToSelector was his idea, I was doing some crazy runtime method addition - this is much cleaner methinks).
here's the code
/**
* This class is specifically useful and intended for testing code paths that branch
* pending implementation of optional methods.
* OCMock does not support mocking of protocols with unimplemented optional methods.
* Further compounding the issue is the fact that OCMock does not allow mocking of
* respondsToSelector (in fact, it does but the behaviour is undefined),
* As such this class can be extending to implement a given protocol, the methods can be mocked/expected
* as normal, but in addition we can tell the class to report it conforms to a protocol method or not.
*
*/
#interface GCOCMockOptionalMethodSupportingObject : NSObject
- (void)markSelectorAsImplemented:(SEL)aSelector;
- (void)unmarkSelectorAsImplemented:(SEL)aSelector;
#end
#import "GCOCMockOptionalMethodSupportingObject.h"
#interface GCOCMockOptionalMethodSupportingObject ()
#property(nonatomic, strong) NSMutableArray *implementedSelectors;
#end
#implementation GCOCMockOptionalMethodSupportingObject {
}
//////////////////////////////////////////////////////////////
#pragma mark init
//////////////////////////////////////////////////////////////
- (id)init {
self = [super init];
if (self) {
self.implementedSelectors = [NSMutableArray array];
}
return self;
}
//////////////////////////////////////////////////////////////
#pragma mark public api
//////////////////////////////////////////////////////////////
- (void)markSelectorAsImplemented:(SEL)aSelector {
if (![self isImplemented:aSelector]) {
[self.implementedSelectors addObject:NSStringFromSelector(aSelector)];
}
}
- (void)unmarkSelectorAsImplemented:(SEL)aSelector {
for (NSString *selectorValue in [self.implementedSelectors mutableCopy]) {
SEL storedSelector = NSSelectorFromString(selectorValue);
if (sel_isEqual(aSelector, storedSelector)) {
[self.implementedSelectors removeObject:selectorValue];
break;
}
}
}
//////////////////////////////////////////////////////////////
#pragma mark private impl
//////////////////////////////////////////////////////////////
- (BOOL)isImplemented:(SEL)aSelector {
for (NSString *selectorValue in self.implementedSelectors) {
SEL storedSelector = NSSelectorFromString(selectorValue);
if (sel_isEqual(aSelector, storedSelector)) {
return YES;
}
}
return NO;
}
//////////////////////////////////////////////////////////////
#pragma mark overridden
//////////////////////////////////////////////////////////////
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([self isImplemented:aSelector]) {
return YES;
} else {
return [super respondsToSelector:aSelector];
}
}
#end
The easiest thing to do is to create a class containing the selectors you do want implemented. There doesn't need to be any implementation. Then you create a class mock of that class instead of a protocol mock and use it just the same way.
For example:
#interface MyAwesomeImplementation : NSObject <MyAwesomeProtocol>
- (void)doThatRequiredThing;
#end
#implementation MyAwesomeImplementation
- (void)doThatRequiredThing {}
#end
id mock = OCMStrictClassMock([MyAwesomeImplementation class]);

Shared UITableViewDelegate

I'm writting a subclass of UITableView and I want my subclass to handle some of the UITableViewDelegate methods itself before passing them along to the "real" delegate as well as forward all the UITableViewDelegate methods not implemented by my subclass.
In the subclass I have a private property:
#property (nonatomic, assign) id <UITableViewDelegate> trueDelegate;
which holds the "real delegate" that all the unimplemented methods should forward to. In both my init methods I set
self.delegate = self;
and I override - (void)setDelegate:(id) like this
-(void)setDelegate:(id<UITableViewDelegate>)delegate {
if (delegate != self) {
_trueDelegate = delegate;
} else {
[super setDelegate:self];
}
}
Then I override these to handle the message forwarding
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSMethodSignature *sig;
sig = [[self.delegate class] instanceMethodSignatureForSelector:aSelector];
if (sig == nil) {
sig = [NSMethodSignature signatureWithObjCTypes:"#^v^c"];
}
return sig;
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = anInvocation.selector;
if ([self respondsToSelector:selector]) {
[anInvocation invokeWithTarget:self];
} else {
[anInvocation invokeWithTarget:_trueDelegate];
}
}
The problem is that the unimplemented delegate methods never get called on the tableview, therefore they are not given a chance to be forwarded along to the _trueDelegate object.
I tried checking for them here:
- (BOOL)respondsToSelector:(SEL)aSelector {
}
but that method is never called for the UITableViewDelegate methods although it catches other methods just fine.
For performance, UITableView checks and remembers which delegate methods are available as soon as the delegate is set. You set the delegate self first, then the trueDelegate. So at the time the delegate is set on the UITableView, trueDelegate is nil, and so -respondsToSelector: on that one always returns NO.
To fix that, set the delegate after trueDelegate is set. Also, you can simplify the forwarding code. Remove all the code you have above except for the property and replace it with:
- (void)setDelegate:(id <UITableViewDelegate>)delegate
{
if (delegate == self) return;
self.trueDelegate = delegate;
[super setDelegate:self];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector]) return YES;
return [self.trueDelegate respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.trueDelegate;
}

#selector and return value

The idea it's very easy, i have an http download class, this class must support the http authentication but it's basically a background thread so i would like to avoid to prompt directly to the screen, i would like to use a delegate method to require from outside of the class, like a viewController.
But i don't know if is possible or if i have to use a different syntax.
This class use this delegate protocol:
//Updater.h
#protocol Updater <NSObject>
-(NSDictionary *)authRequired;
#optional
-(void)statusUpdate:(NSString *)newStatus;
-(void)downloadProgress:(int)percentage;
#end
#interface Updater : NSThread {
...
}
This is the call to the delegate method:
//Updater.m
// This check always fails :(
if ([self.delegate respondsToSelector:#selector(authRequired:)]) {
auth = [delegate authRequired];
}
This is the implementation of the delegate method
//rootViewController.m
-(NSDictionary *)authRequired;
{
// TODO: some kind of popup or modal view
NSMutableDictionary *ret=[[NSMutableDictionary alloc] init];
[ret setObject:#"utente" forKey:#"user"];
[ret setObject:#"password" forKey:#"pass"];
return ret;
}
if ([self.delegate respondsToSelector:#selector(authRequired:)]) {
In ObjC, the colons (:) in the method name is significant. That means authRequired and authRequired: are different methods. Try this instead:
if ([delegate respondsToSelector:#selector(authRequired)]) {