问题
I create new tabbed view project in xcode, in appdelegate I created a protocol
.h file
@protocol myProtocol <NSObject>
-(void)myProtocolMethodOne;
@end
.
.
.
@property (weak) id<myProtocol> mypDelegate;
.m file
@synthesize mypDelegate;
.
.
.
//Inside didFinishLaunchingWithOptions
[mypDelegate myProtocolMethodOne];
In firstViewController & secondViewController (both are displayed as two different tab) I did this in both
AppDelegate *ad = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[ad setMypDelegate:self];
.
.
.
-(void)myProtocolMethodOne
{
NSLog(@"1st VC");
[[self tabBarItem]setBadgeValue:@"ok"];
}
The code is working perfectly but only secondViewController is responding.
I am looking for a broadcasting and listener kind of mechanism using delegates not notifications.
I searched a lot but did find any solutions except this but code is advance for me to understand, so I am taking a step by step approach to understand this by starting form a simple project. Please help me regarding this. How both viewcontrollers can respond to delegate at same time, what should I do?
回答1:
In your case is enough to hold an array with all delegates, by holding an array of delegates, possibly as a private property, and allowing to add/remove delegates:
@interface AppDelegate() // .h file
@property (nonatomic,strong,readwrite) NSMutableArray* delegates;
@end
// .m file
- (void) addDelegate: (id<MyProtocol>) delegate // By convention the first letter should be capital.
{
// Optional code you may need to execute before adding it.
[delegates addObject: delegate];
}
I leave to you the removeDelegate method, it's very simple to implement.
How to call the delegates methods
It's enough to call the selector on every object:
[delegates makeObjectsPerformSelector: myProtocolMethodOne];
If you need to take return values it's better to do it this way:
NSMutableArray* returnValues= [NSMutableArray new];
for(id<MyProtocol> delegate in delegates)
{
id result= [delegate myProtocolMethodTwo]; // Method returning a value
[returnValues addObject: result];
}
How to add the delegates
In every controller (up to N times) you should be able to add it:
AppDelegate *ad = (AppDelegate*)[[UIApplication sharedApplication]delegate];
[ad addDelegate:self];
Additional problem: You may want to have weak delegates, but a NSArray holds strong references. Here you can find a nice solution for this:
NSArray of weak references (__unsafe_unretained) to objects under ARC
It basically says to use NSValue to store weak reference using the valueWithNonretainedObject method.
回答2:
Instead of delegates you might consider something that looks like the Visitor pattern.
@interface MyVisitor : NSObject < myProtocol >
-(void)addAcceptor:(id < myProtocol >)acceptor
@end
@implementation
-(void)myProtocolMethodOne {
[_acceptors enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *break){
[obj performSelector:_sel];
}];
}
// etc etc ... obviously you have to handle return values if you're getting these
@end
回答3:
Since delegate is a simple variable, assigning to it overwrites the value rather than adding to it. You could convert it to an array, but because NSArray keeps strong references to the objects within it you need to deal with potential circular references. (A circular reference in this case is that two objects own each other. Since they're both owned by someone, neither will be freed. Even though they only own each other. Wikipedia has more. But the typical pattern in Objective-C is to make all delegates weak for this reason.)
Instead of delegates, you may wish to consider using NSNotificationCenter
notifications.
Instead of 1:1, they're 1:any (including 0 without special considerations). The idea is that one object posts a notification, and the objects that are interested in it observe it. Each object can pick which events they're interested in.
There's a few steps you'll need to perform:
- Remove all the delegate code you've written.
- Agree on a notification name.
- Register the objects that will respond to the notification. (This is where you set the delegate.)
- Handle the notification.
- Post the notification (where you previously called the delegate).
- Unregister the objects when they're destroyed.
What you would do is agree on a key, probably in a constant.
Keys.h:
extern NSString *MethodOneNotification;
Keys.m:
NSString *MethodOneNotification = @"MethodOneNotification";
Then register in firstViewController
and secondViewController
like this somewhere like viewDidLoad
:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(methodOneNotification:) object:nil];
Provide a handler in both firstViewController
and secondViewController
for the selector:
- (void)methodOneNotification:(NSNotification *)notification {
NSLog(@"%@ got the notification!", [self class]);
}
Call the notification where you previously called the delegate:
[[NSNotificationCenter defaultCenter] postNotificationName:MethodOneNotification
object:nil];
And in both firstViewController
and secondViewController
, you'll want to remove the notification registration (certainly in dealloc):
- (void)dealloc {
[[NSNotification defaultCenter] removeObserver:self name:MethodOneNotification
object:nil];
// [super dealloc] if you're not using ARC, but you should be
}
Within the notification handler, you can access the notification's sender as notification.object
. If you need to pass information along with the notification, you can use a different variant of postNotification:
that accepts a NSDictionary
, then you can access the dictionary as notification.userInfo
.
If you need to return values to the object that posted the messages, you'll have to send them back by sending messages to the poster (which you have access to as notification.object
). For instance:
- (void)methodOneNotification:(NSNotification *)notification {
AppDelegate *appDelegate = notification.object;
[appDelegate returningValue:1];
}
Here, obviously, AppDelegate would need to define and handle -(void)returningValue:(int)value
.
You'll need to keep the return value on the class instance. Of course, if you have multiple returns possible, you'll need to collect those in returningValue:
with an array. But at least you've skipped circular references.
The other way to solve this is with blocks. That would double the size of this answer, though. :) Bottom line, though: The delegate pattern is the wrong pattern for this problem. Luckily, the others are easy to pick up.
来源:https://stackoverflow.com/questions/14785201/objective-c-multicasting-delegates