© 2013 Yan Rabovik (@rabovik on twitter)
FastElegantDelegation solves 3 problems:
- Elegant single delegation without annoying
respondsToSelector:
checks; - Implementing multi-delegate pattern in your own code;
- Assigning multiple delegates for a single third-party source.
Delegation is often implemented like this:
@protocol SomeDelegateProtocol <NSObject>
@optional
-(void)someOptionalDelegateMethod;
// ...
@end
@interface MyClass : NSObject
@property (nonatomic,weak) id<SomeDelegateProtocol> delegate;
@end
@implementation MyClass
-(void)someMethod{
if ([self.delegate respondsToSelector:@selector(someOptionalDelegateMethod)]) {
[self.delegate someOptionalDelegateMethod];
}
}
// ...
@end
With FastElegantDelegation you may get rid of annoying respondsToSelector:
checks:
@implementation MyClass
fed_use_proxy_for_delegate // just add this macro
-(void)someMethod{
[self.delegate someOptionalDelegateMethod];
}
// ...
@end
fed_use_proxy_for_delegate
macro synthesizesdelegate
andsetDelegate
methods;delegate
returns aFEDProxy
class that forwards messages to the real delegate. If delegate does not respond to an optional protocol method, proxy just does nothing like when the message is sent tonil
.
You may use any name you like for the delegate property, but you should specify getter and setter names in the macro:
@interface MyClass : NSObject
@property (nonatomic,weak) id<SomeDelegateProtocol> myCoolDelegate;
@end
@implementation MyClass
fed_use_proxy_for_property(myCoolDelegate,setMyCoolDelegate)
// ...
@end
Delegate methods may return any value.
@implementation MyClass
fed_use_proxy_for_delegate
-(void)someMethod{
BOOL returnValue = [self.delegate methodReturningBOOL];
}
// ...
@end
If the property is declared as weak
then delegate is not retained. When delegate deallocates, proxy is automatically deallocated too. You will never get into a situation when proxy returned by self.delegate
is alive, but real delegate is already deallocated.
FEDProxy
does not break required/optional paradigm. If you try to call required method that is not implemented in the delegate, you'll get an exception.
If for some reason you need to declare delegate as strong, it is OK. FEDProxy
will be strongly stored in the ivar and will retain real delegate.
@interface MyClass : NSObject
@property (nonatomic, strong) id<SomeDelegateProtocol> myStrongDelegate;
@end
FEDProxy
uses fast message forwarding (via forwardingTargetForSelector:
) and a cache to prevent multiple internal respondsToSelector:
checks.
With fed_synthesize_multi_delegates
macro you may implement multi-delegate pattern in your own class so that the clients of your class may easily add and remove delegates.
Example:
@class MyClass;
@protocol MyClassDelegate <NSObject>
-(void)myClassDidStartSomeJob;
@end
@interface MyClass : NSObject
-(void)addDelegate:(id<MyClassDelegate>)delegate;
-(void)removeDelegate:(id<MyClassDelegate>)delegate;
@end
@implementation MyClass
fed_synthesize_multi_delegates(MyClassDelegate)
-(void)someJob{
[self.delegates myClassDidStartSomeJob]; // will be sent to each delegate
}
@end
fed_synthesize_multi_delegates
synthesizes addDelegate:
, removeDelegate:
and delegates
methods.
You can name methods as you like. For example you may use listener
name instead of delegate
:
@class MyClass;
@protocol MyClassListener <NSObject>
-(void)myClassDidStartSomeJob;
@end
@interface MyClass : NSObject
-(void)addListener:(id<MyClassListener>)delegate;
-(void)removeListener:(id<MyClassListener>)delegate;
@end
@implementation MyClass
fed_synthesize_multiproxy(MyClassListener, addListener, removeListener, listeners)
-(void)someJob{
[self.listeners myClassDidStartSomeJob];
}
@end
If a method returns a value the return value will be from the first delegate in the list that responds to the selector.
FEDMultiProxy
class is a NSProxy
subclass that allows to add multiple delegates to a single third-party source.
For example, you may assign multiple delegates to a UIScrollView:
__typeof(self) __weak weakSelf = self;
FEDMultiProxy *multiProxy = [FEDMultiProxy proxyWithDelegates:@[firstDelegate, secondDelegate]
protocol:@protocol(UIScrollViewDelegate)
retainedByDelegates:YES
onDealloc:^{
weakSelf.scrollView.delegate = nil;
}];
self.scrollView.delegate = multiProxy;
You do not need to keep a strong reference to FEDMultiProxy
object. It is automatically retained by each delegate and will be deallocated when all delegates die. onDealloc
block will be called at that moment and you may set targets delegate to nil
there.
Add FastElegantDelegation
to your Podfile.
- iOS 5.0+
- ARC
- MAObjCRuntime
MIT License.