NSURLSession delegation: How to implement my custom SessionDelegate class accordingly? - objective-c

Got a singleton class, so called RequestManager, which shall handle requests made by different modules and background tasks of my application.
#interface RequestFactory : NSObject
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender;
...
#end
Then I got another class, so called SessionDelegate, which shall handle all the callbacks during the request.
#interface SessionDelegate : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
#property (weak, nonatomic) id <RequestFactoryDelegate> delegate;
#end
My idea is to encapsulate the functions in these classes to not overload my classes, because I need a lot of helper classes with CommonCrypto and so on.
So I set quickly coded a protocol RequestFactoryDelegate to send the received data to the sender who initiated the origin request.
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
self.sessionDelegate.delegate = sender;
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
[dataTask resume];
}
Well, it works if I have an object, let us call it senderA which sends the requests, because the set delegate is always senderA itself.
The problem occurs having another object, e.g. senderB which sends requests - not even at the same time - but very shortly after senderA send.
- (void)foo
{
[requestFactory requestDataWith:token
id:id
sender:senderA]; // let's assume this takes 20s
[requestFactory requestDataWith:token
id:id
sender:senderB]; // let's assume this takes 1s
}
Because the request of senderA is still in progress, senderB sets the delegate to him and what happens is the delegate function of senderB is run twice.
<senderB>
<senderB>
Well... I really need to implement an own custom delegate (whether or not in the same class as the RequestFactory or not), but how to I handle the callback methods so I can respond properly to either senderA or senderB?
My last idea is to override the NSURLSessionTasks class and implement an own delegate property or block property or whatever.
Many thanks in advance.

You can attach an arbitrary object to a task:
NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
id whatever = // anything at all!
[NSURLProtocol setProperty:whatever forKey:#"thing" inRequest:req];
NSURLSessionDownloadTask* task = [[self session] dataTaskWithRequest:req];
And retrieve it later (in the delegate) like this:
NSURLRequest* req = task.originalRequest;
id thing = [NSURLProtocol propertyForKey:#"thing" inRequest:req];
The value here (whatever) can be any object. It can be a completion handler callback - I've done this and it works just fine.

In Objective-C, there is an alternative to subclassing that might be what you want here: associating objects.
It works like this: you can "attach" (associate) an object to another object with a custom key and later retrieve it. So in your case, you would do something like:
#include <objc/runtime.h>
// Top level of your .m file. The type and content of this
// variable don't matter much, we need the _address_ of it.
// See the first link of this answer for details.
static char kDelegateKey = 'd';
- (void)requestDataWith:(NSString *)token
id:(NSString *)id
sender:(id<RequestFactoryDelegate>)sender
{
NSMutableURLRequest *request = //create the request here
NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithRequest:request];
// Associate the sender with the dataTask. We use "assign" here
// to avoid retain cycles as per the delegate pattern in Obj-C.
objc_setAssociatedObject(dataTask, &kDelegateKey, sender, OBJC_ASSOCIATION_ASSIGN);
[dataTask resume];
}
- (void)someOtherMethodWithDataTask:(NSURLSessionDataTask *)dataTask
{
// Read the attached delegate.
id<RequestFactoryDelegate> delegate = objc_getAssociatedObject(dataTask, &kDelegateKey);
// Do something with the delegate.
}

Here is my solution.
I just just use the unique identifier of each sessionTask object. So my delegate object contains a dictionary with blocks as values to execute on success/failure and the identifier as keys to identify the correct execution block.
In the .h file I declared the dictionary and a method to add a key/value object:
#property (nonatomic, strong) NSDictionary *completionHandlerDictionary;
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString *)identifier;
And in the .m file I call the handler block.
- (void)addCompletionHandler:(CompletionHandlerType)handler
forTask:(NSString*)identifier
{
if ([self.completionHandlerDictionary objectForKey:identifier]) {
NSLog(#"Error: Got multiple handlers for a single task identifier. This should not happen.\n");
}
[self.completionHandlerDictionary setObject:handler forKey:identifier];
}
- (void)callCompletionHandlerForTask:(NSString *)identifier
{
CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];
if (handler) {
[self.completionHandlerDictionary removeObjectForKey: identifier];
NSLog(#"Calling completion handler.\n");
handler();
}
}
That's it, simple as it is.

Related

completionHandler definition inside handleEventsForBackgroundURLSession:

This is not a trivial question asked here in StackOverFlow before, at least I haven’t found anything similar, of course I also googled it and read most of high ranked results.
BTW, if any folks here don't feel comfortable with Objective C’s block syntax, visit this page please
http://fuckingblocksyntax.com ,
before throwing any block related issues.
1st part of my question is: the background of declaration of block-parameter, as well as invoking a method which has a block-parameter ( in many cases, a completionBlock )
The “calleE-method" in MyWorker class:
… ...
#implementation MyWorker
-(void) aWorkerMethodNeedsABlockInput: ((void)(^)( NSObject *, double )) blockParam
{
NSObject *anObj=[[ NSObject alloc] init];
double *aDouble;
[self retrieveTimeConsumingResults: anObj withNumberOfTry: aDouble ];
blockParam ( anObj, * aDouble );
}
#end
The “calleR-method" in MyManager class:
#interface myManager()
#property (nonatomic) MyWorker * mWorker;
#property (nonatomic, copy) (void)(^mBlockProperty)( NSObject *, double );
#end
#implementation MyManager
-(void) aManagerMethodWhoCallsWorkerWithCompletionHandler
{
(void)(^ valBlock )( NSObject *, double ) = ^(void)( NSObject * realObj, double realDouble )
{
[realObj performSelector:#SEL( aSelector) withObject: #(realDouble) afterDelay: aTimeInterval];
} ;
self.mBlockProperty=[valBlock copy];
[self.mWorker aWorkerMethodNeedsABlockInput : self.mBlockProperty];
}
#end
above sudo-code was the NORMAL way, in our custom code, of storing a block inside property, declaring a block parameter and also offering block’s arguments in CALLEE; providing block definition and also “consuming” block’s arguments in the CALLER. I keep 'void' returnType in writing for clarity of block-syntax. Correct my writing if I did wrong, please!
2nd part of my question:
the routine usage of
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
NSLog(#"Handle events for background url session");
self.backgroundSessionCompletionHandler = completionHandler;
}
then later
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
WebAppDelegate *appDelegate = (WebAppDelegate *)[[UIApplication sharedApplication] delegate];
if (appDelegate.backgroundSessionCompletionHandler) {
void (^completionHandler)() = appDelegate.backgroundSessionCompletionHandler;
appDelegate.backgroundSessionCompletionHandler = nil;
completionHandler();
}
NSLog(#"All tasks are finished");
}
the background callback via the daemon works in above pattern based on NSURLSession framework, right? I did it many times, not a problem on applying such pattern.
Which I have been wondering for a long time is:
What is really inside the definition of the completionHandler parameter of “handleEventsForBackgroundURLSession:” method, when the method is invoked from a block-property storage? < at the time when “ completionHandler();” is executed >
I have never seen any sample/demo which put/copy any block-of-code into completionHandler... or I wish to know too much?
What is really inside the definition of the completionHandler parameter of “handleEventsForBackgroundURLSession:” method, when the method is invoked from a block-property storage? < at the time when “ completionHandler();” is executed > I have never seen any sample/demo which put/copy any block-of-code into completionHandler... or I wish to know too much?
If I understand your question correctly, you are asking what implementation is inside the block that is passed to an application's implementation of the UIApplicationDelegate method application:handleEventsForBackgroundURLSession:completionHandler: by the system.
application:handleEventsForBackgroundURLSession:completionHandler: is invoked (indirectly) by an external service process. When an application uses NSURLSession to create a background session, that session is managed by that system service. That service does the actual background transfer and notifies UIKit/Foundation and in turn your application through a mechanism called XPC. XPC is widely used by MacOS developers, but at this time is not directly available to iOS applications - however many of the APIs and services used by developers on iOS are actually communicating with XPC services.
In the case of application:handleEventsForBackgroundURLSession:completionHandler:, the block passed to the completionHandler parameter is an opaque callback. The background transfer service needs to know when your application is done handling events for the session. Invoking that block informs the service that the application has completely processing of this set of events and the daemon can move on.
The block is created and owned by the system and as such an application should not attempt to modify or change it (other than copying the block, which is the right thing to do!). Applications should also not provide their own completion blocks - a developer-provided block would have no way to inform the transfer service of completion unless it wrapped the block passed to completionHandler: itself.
The background transfer service and NSURLSession were introduced in iOS 7. If you are writing a third party framework or library it can be very beneficial to take advantage of the service, but the framework must provide a way to handle events for any background session it owns. Perhaps because of this only a few third party libraries seem to support background transfers. Supporting this is not difficult to do - the library only needs a method to indicate ownership of the session, and a method to take the completion block and handle the events:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {
if ([someCloudFrameworkObject canHandleEventsForSessionWithIdentifier:identifier]){
[someCloudFrameworkObject handleEventsForBackroundSessionWithIdentifier:identifier completionHandler:completionHandler];
}
}

Bad Access Parent is Null - Wht is this Happening?

I understand what the error is, but in this case not what is causing it. In general use it occurs maybe 1% of the time (probably less) but I have found an extreme way to cause it which I will describe below. First, I am using an in-app purchase process I found on Ray Wenderlich's site. Below are the specific pieces of concern here:
.h:
typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);
#interface IAPHelper : NSObject
- (void)requestProductsWithCompletionHandler:RequestProductsCompletionHandler)completionHandler;
#end
.m
#implementation IAPHelper
{
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
_completionHandler(YES, skProducts); // here is where bad access occurs
_completionHandler = nil;
}
Again, 99%+ of the time this works just fine. Given how infrequent the bad access happens in regular use and it has been difficult to diagnose. However, I found an extreme way to cause the issue. The setup is "Tab 1" is a table view controller and "Tab 2" is a table view controller that uses the code from above. If I quickly switch back and forth between the two tabs I can usually cause the problem to occur anywhere from a few seconds into it to 20-30 seconds. Doesn't happen every time in this scenario but it does the vast majority. As marked above the following line gets a bad access error with Parent is Null.
_completionHandler(YES, skProducts);
To solve the issue I simple do the following:
if (_completionHandler)
{
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
While that fix does work and does solve the problem I am still bothered by why this is occurring. Any thoughts as to the cause of this?
Update:
Apologies to all as I did forget to include the following in what I pasted above.
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
You need to treat your completion block as any other object when you are storing it. So if you are storing your block as a variable and then using it within a different scope from where you assign it, you need to increment the reference count by either copying it or retaining it. The simple solution is to create a strong property to store your block.
Depending on unseen bits of code, your completion handler block might not be being assigned correctly. You need to copy a block if you intend to use it outside of the scope in which it was created.
In your interface, declare your completion handler's storage attribute as "copy".
#property (nonatomic, readwrite, copy) void (^completionHandler)(BOOL, NSArray *);
If you want to control the local variable, you can synthesize the property manually in your implementation:
#synthesize completionHandler = _completionHandler;

Setting/getting global variables in objective-C

I am writing an app which is a sort of dictionary - it presents the user with a list of terms, and when clicked on, pops up a dialog box containing the definition. The definition itself may also contain terms, which in turn the user can click on to launch another definition popup.
My main app is stored in 'myViewController.m'. It calls a custom UIView class, 'CustomUIView.m' to display the definition (this is the dialog box that pops up). This all works fine.
The text links from the CustomUIView then should be able to launch more definitions. When text is tapped in my CustomUIView, it launches another CustomUIView. The problem is, that this new CustomUIView doesn't have access to the hash map which contains all my dictionary's terms and definitions; this is only available to my main app, 'myViewController.m'.
Somehow, I need to make my hash map, dictionaryHashMap, visible to every instance of the CustomUIView class. dictionaryHashMap is created in myViewController.m when the app opens and doesn't change thereafter.
I don't wish to limit the number of CustomUIViews that can be opened at the same time (I have my reasons for doing this!), so it would be a little resource intensive to send a copy of the dictionaryHashMap to every instance of the CustomUIView. Presumably, the solution is to make dictionaryHashMap a global variable.
Some of my code:
From myViewController.m:
- (void)viewDidLoad
{
self.dictionaryHashMap = [[NSMutableDictionary alloc] init]; // initialise the dictionary hash map
//... {Code to populate dictionaryHashMap}
}
// Method to pop up a definition dialog
- (void)displayDefinition:(NSString *) term
{
NSArray* definition = [self.dictionaryHashMap objectForKey:term]; // get the definition that corresponds to the term
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
[definitionPopup setTitle: term];
[definitionPopup setMessage: definition];
[definitionPopup show];
}
// Delegation for sending URL presses in CustomUIView to popupDefinition
#pragma mark - CustomUIViewDelegate
+ (void)termTextClickedOn:(CustomUIView *)customView didSelectTerm:(NSString *)term
{
myViewController *t = [[myViewController alloc] init]; // TODO: This instance has no idea what the NSDictionary is
[t displayDefinition:term];
}
From CustomUIView.m:
// Intercept clicks on links in UIWebView object
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
[myViewController termTextClickedOn:self didSelectTerm:request];
return NO;
}
return YES;
}
Any tips on how to make the dictionaryHashMap visible to CustomUIView would be much appreciated.
I have tried making the dictionaryHashMap global by doing the following:
Changing all instances of 'self.dictionaryHashMap' to 'dictionaryHashMap'
Adding the line 'extern NSMutableDictionary *dictionaryHashMap;' to CustomUIView.h
Adding the following outside of my implementation in myViewController.m: 'NSMutableDictionary *dictionaryHashMap = nil;'
However, the dictionaryHashMap remains invisible to CustomUIView. As far as I can tell, it actually remains a variable which is local to myViewController...
It's not resource-intensive to pass around the reference (pointer) to dictionaryHashMap. A pointer to an object is only 4 bytes. You could just pass it from your view controller to your view.
But I don't know why you even need to do that. Your view is sending a message (termTextClickedOn:didSelectTerm:) to the view controller when a term is clicked. And the view controller already has a reference to the dictionary, so it can handle the lookup. Why does the view also need a reference to the dictionary?
Anyway, if you want to make the dictionary a global, it would be more appropriate to initialize it in your app delegate, in application:didFinishLaunchingWithOptions:. You could even make the dictionary be a property of your app delegate and initialize it lazily.
UPDATE
I didn't notice until your comment that termTextClickedOn:didSelectTerm: is a class method. I assumed it was an instance method because myViewController starts with a lower-case letter, and the convention in iOS programming is that classes start with capital letters. (You make it easier to get good help when you follow the conventions!)
Here's what I'd recommend. First, rename myViewController to MyViewController (or better, DefinitionViewController).
Give it a property that references the dictionary. Whatever code creates a new instance of MyViewController is responsible for setting this property.
Give CustomUIView properties for a target and an action:
#property (nonatomic, weak) id target;
#property (nonatomic) SEL action;
Set those properties when you create the view:
- (void)displayDefinition:(NSString *)term {
NSArray* definition = [self.dictionaryHashMap objectForKey:term];
CustomUIView* definitionPopup = [[[CustomUIView alloc] init] autorelease]; // initialise a custom popup
definitionPopup.target = self;
definitionPopup.action = #selector(termWasClicked:);
...
In the view's webView:shouldStartLoadWithRequest: method, extract the term from the URL request and send it to the target/action:
- (BOOL)webView: (UIWebView*)webView shouldStartLoadWithRequest: (NSURLRequest*)request navigationType: (UIWebViewNavigationType)navigationType {
if ( navigationType == UIWebViewNavigationTypeLinkClicked ) {
NSString *term = termForURLRequest(request);
[self.target performSelector:self.action withObject:term];
return NO;
}
return YES;
}
In the view controller's termWasClicked: method, create the new view controller and set its dictionary property:
- (void)termWasClicked:(NSString *)term {
MyViewController *t = [[MyViewController alloc] init];
t.dictionary = self.dictionary;
[t displayDefinition:term];
}
Create a class that will be used as singleton. Example.
You Should always keep your data in separate class as the mvc pattern suggest and that could be achieved by using a singleton class for all your dictionary terms and accesing them from every custom view when needed.

Objective-C blocks usage

I have been looking around online, doing research into how to use blocks. I have also decided to set up a basic example to try and understand the way in which they work.
Essentially what I want to do is have a 'block variable' (no sure if thats the correct term) in which I can store a block of code. I then want to be able to set the code in this block at pointX (methodA or methodB) in my code, then run the block of code at pointY (methodX).
So to be specific, my question is 3-fold
Using the example below is the setup / usage of blocks correct and valid?
In methodX how do I execute the code inside the block (self.completionBlock)?
When creating the block in methodA and methodB will the code be called there and then? If so how can I stop this from happening (all I want to do is set up the code in the block to be called later)?
I may have completely misunderstood how blocks are used, apologies if this is the case, however I'm relatively new to Objective-C and I'm trying to learn.
Here is my code so far:
.h
typedef void (^ CompletionBlock)();
#interface TestClass : NSObject
{
CompletionBlock completionBlock;
NSString *stringOfText;
NSString *otherStringOfText;
}
#property(nonatomic, copy)CompletionBlock completionBlock;
#property(nonatomic, retain)NSString *stringOfText;
#property(nonatomic, retain)NSString *otherStringOfText;
- (void)methodA:(NSString *)myText;
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
- (void)methodX;
#end
.m
- (void)methodA:(NSString *)myText;
{
if ([self.stringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodA"; // normally make use of myText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodA:blocksafeSelf.stringOfText];
blocksafeSelf.stringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText
}
}
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
{
if ([self.stringOfText isEqualToString:#""] || [self.otherStringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodB"; // normally make use of myText
self.otherStringOfText = #"I also visited methodB"; // normally make use of myOtherText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodB:blocksafeSelf.stringOfText and:blocksafeSelf.otherStringOfText];
blocksafeSelf.stringOfText = nil;
blocksafeSelf.otherStringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText and self.otherStringOfText
}
}
- (void)methodX
{
// At this point run the block of code in self.completionBlock...how?!
}
In my example either methodA or methodB will be called first. Then some time later (perhaps from a different class) methodX will be called (only ever after methodA or methodB have been called).
It's worth noting that the methods methodA, methodB and methodX are all in a singleton class.
NOTE: This is just a dummy example to try and understand the workings of blocks, I'm fully aware there are other ways to achieve the same result.
Here's the code, just to be clear:
- (void)methodX
{
if(self.completionBlock)
self.completionBlock();
}
I think you want to do self.completionBlock(); in methodX.

delegate for a singleton object

I have a NSObject which is a singleton. Is there any issue of having a delegate for this singleton class? I am worried that it would fail for a singleton type.
Here's my scenario. I have a function (inside this singleton class) that does a async request to pull out a NSDictionary from an API. Basically when this request is done I want to notify a class that the request has finished.
No, a delegate wouldn't fail, but consider using NSNotificationCenter instead:
static NSString *const kMyClassNotificationName = #"myClassNotificationName";
// where you would call a delegate method (e.g. [self.delegate doSomething])
[[NSNotificationCenter defaultCenter] postNotificationName:kMyClassNotificationName object:self userInfo: /* dictionary containing variables to pass to the delegate */];
// where you would set up a delegate (e.g. [Singleton instance].delegate = self)
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSomething) name:kMyClassNotificationName object:[Singleton instance]];
You have basically three options:
Use a delegate. A singelton is a objetct, so of couse it can have a delegate. If several objects whants to use it and needs to set themselves as delegates, you can reset them each time, but that might get hairy.
Use notifications, as shown by Richard J. Ross III., but seriously: It seems to be strange to me, if you have a singleton, that needs to inform one delegate, but you'd use a broadcasting technology.
use completion blocks, where the calling objects passes a block to the singleton, that gets executed, once the singleton fulfilled a task. See [NSURLConnection sendAsynchronousRequest:queue:completionHandler:] (ok, this is not a singleton, but a class method. The principle is the same),that uses one completion block, or the great AFNetworking, that uses a success and a failure block.
From it's example codes:
[[AFGowallaAPIClient sharedClient] getPath:urlString
parameters:mutableParameters
success:^(__unused AFHTTPRequestOperation
*operation,
id JSON)
{
NSMutableArray *mutableRecords = [NSMutableArray array];
for (NSDictionary *attributes in [JSON valueForKeyPath:#"spots"]) {
Spot *spot = [[[Spot alloc] initWithAttributes:attributes] autorelease];
[mutableRecords addObject:spot];
}
if (block) {
block([NSArray arrayWithArray:mutableRecords]);
}
} failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
if (block) {
block([NSArray array]);
}
}];
There is nothing wrong with having a delegate for a singleton, but it does create a lot of edge cases that you need to handle. Such as:
If object A calls setDelegate:, followed immediately by object B calling setDelegate: then object A will never receive delegate calls.
You need to check whether you are the delegate before unsetting the singleton's delegate. Typically in dealloc you call singleton.delegate = nil;. If another object happened to become delegate after you did, then you just caused caused them to unexpectedly stop being delegate.
Singletons with delegates is not a well-established pattern. Your solutions should vary depending on how robust your use case is. Here are some solutions (in order of easiest -> most robust).
Keep it simple
Design your App to never have multiple objects being the singleton's delegate at the same time (this may be impossible).
NSNotification
Use NSNotificationCenter to signal events instead of delegation. See some of the other answers posted in this thread.
Multiple Delegates
Extend your singleton to support multiple delegate. Replace setDelegate: with: addDelegate: and removeDelegate:
#property (atomic) NSMutableArray *delegates;
- (void)addDelegate:(NSObject * <YourProtocol>)foo {
[self.delegates addObject:foo];
}
- (void)removeDelegate:(NSObject * <YourProtocol>)foo {
[self.delegates removeObject:foo];
}
- (void)signalDelegateEvent {
[self.delegates enumerateObjectsUsingBlock:^(id<YourProtocol> obj,
NSUInteger idx,
BOOL *stop) {
// call delegate method `foo` on each delegate
if ( [obj respondsToSelector:#selector(foo)]) {
[obj foo];
}
}];
}
I have used the multi-delegate pattern successfully in many apps. Be careful to think about how multi-threading effects things if you choose this approach.