Objective-C组播代表
我在xcode中创build新的标签视图项目,在appdelegate中创build了一个协议
.h文件
@protocol myProtocol <NSObject> -(void)myProtocolMethodOne; @end . . . @property (weak) id<myProtocol> mypDelegate;
.m文件
@synthesize mypDelegate; . . . //Inside didFinishLaunchingWithOptions [mypDelegate myProtocolMethodOne];
在firstViewController&secondViewController(都显示为两个不同的选项卡),我在这两个
AppDelegate *ad = (AppDelegate*)[[UIApplication sharedApplication]delegate]; [ad setMypDelegate:self]; . . . -(void)myProtocolMethodOne { NSLog(@"1st VC"); [[self tabBarItem]setBadgeValue:@"ok"]; }
代码工作正常,但只有secondViewController响应。
我正在寻找一种使用委托而不是通知的广播和监听器机制。
我search了很多,但find了除此之外的任何解决scheme,但是代码提前让我理解,所以我正在一步一步地从一个简单的项目开始理解这一点。 请帮助我这一点。 视图控制器如何同时响应委托,我该怎么做?
在你的情况下,足以容纳一个数组与所有代表,通过持有代表数组,可能作为一个私人财产,并允许添加/删除代表:
@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]; }
我留给你removeDelegate方法,这是非常简单的实现。
如何调用委托方法
在每个对象上调用select器就足够了:
[delegates makeObjectsPerformSelector: myProtocolMethodOne];
如果你需要返回值,最好这样做:
NSMutableArray* returnValues= [NSMutableArray new]; for(id<MyProtocol> delegate in delegates) { id result= [delegate myProtocolMethodTwo]; // Method returning a value [returnValues addObject: result]; }
如何添加代表
在每个控制器(最多N次)中,您应该可以添加它:
AppDelegate *ad = (AppDelegate*)[[UIApplication sharedApplication]delegate]; [ad addDelegate:self];
额外的问题 :你可能想要弱代理,但是NSArray拥有强大的引用。 在这里你可以find一个很好的解决scheme:
弱引用的NSArray(__unsafe_unretained)到ARC下的对象
它基本上说,使用valueWithNonretainedObject方法使用NSValue存储弱引用。
您可能会考虑与访问者模式类似的内容,而不是代表。
@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
由于委托是一个简单的variables,赋值给它覆盖的价值,而不是添加到它。 你可以将它转换为一个数组,但是由于NSArray保持对它内部对象的强引用,所以你需要处理潜在的循环引用。 (在这种情况下,循环引用是两个对象相互拥有,既然它们都属于某个人,那么它们也不会被释放,即使它们只是相互拥有对方, 维基百科也有更多的东西 ,但Objective-C中的典型模式是让所有代表都软弱无力。)
代替代表,您可能希望考虑使用NSNotificationCenter
通知。
而不是1:1,他们是1:任何(包括0没有特别的考虑)。 这个想法是,一个对象发布通知,并感兴趣的对象观察它。 每个对象可以select他们感兴趣的事件。
您需要执行以下几个步骤:
- 删除你写的所有委托代码。
- 同意通知名称。
- 注册将响应通知的对象。 (这是你设置委托的地方。)
- 处理通知。
- 发布通知(以前称为委托的地方)。
- 取消注册对象时,他们被摧毁。
你要做的是同意一个关键,可能是一个常数。
Keys.h:
extern NSString *MethodOneNotification;
Keys.m:
NSString *MethodOneNotification = @"MethodOneNotification";
然后注册firstViewController
和secondViewController
像这样viewDidLoad
:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(methodOneNotification:) object:nil];
在select器的firstViewController
和secondViewController
提供一个处理程序:
- (void)methodOneNotification:(NSNotification *)notification { NSLog(@"%@ got the notification!", [self class]); }
调用之前称为委托的通知:
[[NSNotificationCenter defaultCenter] postNotificationName:MethodOneNotification object:nil];
在firstViewController
和secondViewController
,你都要删除通知注册(当然在dealloc中):
- (void)dealloc { [[NSNotification defaultCenter] removeObserver:self name:MethodOneNotification object:nil]; // [super dealloc] if you're not using ARC, but you should be }
在通知处理程序中,您可以通知notification.object的发件人。 如果您需要传递信息以及通知,则可以使用postNotification:
的不同变体postNotification:
接受NSDictionary
,然后可以通过notification.userInfo
访问字典。
如果您需要将值返回给发布消息的对象,则必须通过将消息发送给发布者(您可以通过notification.object
访问)来发回消息。 例如:
- (void)methodOneNotification:(NSNotification *)notification { AppDelegate *appDelegate = notification.object; [appDelegate returningValue:1]; }
在这里,显然,AppDelegate需要定义和处理-(void)returningValue:(int)value
。
你需要保持类实例的返回值。 当然,如果你有多个可能的返回值,你需要通过一个数组来收集returningValue:
。 但至less你已经跳过循环引用。
解决这个问题的另一种方法是使用块。 这将会使这个答案的大小加倍。 :)底线,虽然:委托模式是这个问题的错误模式。 幸运的是,其他人很容易接受。