我创建了一个库,可以下载 JSON 数据,然后将其放入 NSDictionary 中。我用一个简单的 Twitter 引擎来包装这个类,它允许我拉动我朋友的时间线、发布更新并发布带有我的 GPS 位置的更新。根据我对 Objective-C 的有限经验,连接一切的方法是通过委托。我设置了一个委托属性,它将异步结果回调到选择器或方法签名。我什至可以在委托上创建一个可选或必需的接口,这将允许 Xcode 帮助我实现委托。为了了解如何在 Objective-C 中使用委托,我创建了这个简单的项目。
http://www.smallsharptools.com/downloads/ObjC/Delegates.zip
它定义了一个 Worker 类,允许您使用委托初始化该类。当使用 doWork 方法完成工作时,它会在委托上查找方法签名以将消息发送回它。它使用以下代码。
if([[self delegate] respondsToSelector:@selector(workFinished:)]) {
NSString *msg = @"That's it? Easy!";
[[self delegate] workFinished:msg];
}
它寻找 workFinished: 方法来传回消息。我将此方法签名声明为可选接口,并在标头 Worker.h 中使用以下代码。
@protocol WorkerNotifications
@optional
- (void) workFinished: (NSString *) msg;
@end
您可以从下载中查看该项目的其余部分,了解所有详细信息。但这两个代码片段展示了这种委托模式的工作原理。但对于 Twitter 类,我需要知道启动异步操作的方法的上下文,该操作会导致对委托方法的回调。如果我从调用类多次调用 sendUpdate 方法,我应该如何知道回调的上下文?
通常,使用 JavaScript、Java 或 C# 等语言,我会创建一个内联闭包或匿名类,它们可以访问起始上下文,但目前 iPhone 上的 Objective-C 还不可能做到这一点。我发现这个问题已经在 StackOverflow 上被提出和回答了。
Objective-C 中的匿名委托实现?
所以我所做的是跳过可选接口,而是传入一个选择器,Twitter 类将在异步操作完成时调用该选择器。启动此操作的调用看起来像...
CMTwitterEngine *engine = [[CMTwitterEngine alloc] initWithDelegate:self];
[engine setSendUpdateFinished:@selector(sendUpdateFinished:)];
[engine setSendUpdateFailed:@selector(sendUpdateFailed:)];
[engine setParsingSendUpdateFailed:@selector(parsingSendUpdateFailed:)];
[engine setUsername:TWITTER_USERNAME pass:TWITTER_PASSWORD];
[engine sendUpdate:statusUpdateText.text];
此代码首先使用 self 作为委托来初始化引擎引用。为了附加回调,我发送了最初在 sendUpdate 方法签名上使用的选择器,但方法调用变得相当长。我选择简单地设置选择器的属性。这一切都有效,但我不确定我喜欢它是如何工作的,因为它只能部分解决我的问题。
为了完成这个示例,我完成了异步工作,并最终在内部调用一个方法,该方法查找给定的选择器,如果定义了它,则调用它。
- (void)sendUpdateFinished:(NSDictionary *)dictionary {
if (self.sendUpdateFinished != nil) {
[self.delegate performSelector:self.sendUpdateFinished withObject:dictionary];
}
}
我可以传入状态消息作为 Twitter 更新发送,但我仍然没有原始呼叫的上下文。如果我想多次调用 sendUpdate 并且第一个异步调用仍在运行怎么办?如果第二个调用先完成怎么办?它们都将 self 作为委托,因此我必须以某种方式跟踪上下文,或者将它们传递给不同的选择器来区分它们,这也不能满足我的需求。如果我有 3、4 或 5 个异步调用,会发生什么情况?我需要知道哪些已成功发送以及何时完成。
看来我能做到这一切的唯一方法是创建一个类,该类保存上下文所需的所有属性,让该类充当调用异步 Twitter 方法的委托,然后向父级报告类可能是 UIViewController。我会采用这种方法,但我还没有阅读过这种方法,也没有看到任何执行此操作的示例代码。
你会怎么办?您将如何处理发出的多个异步调用,这些调用可能以不同的顺序结束,然后在完成后使用上下文处理它们?
I have created a library which can download JSON data which is then placed into an NSDictionary. I wrap this class with a simple Twitter engine which allows me to pull my friends timeline, post an update and post an update with my GPS location. From my limited experience with Objective-C the way to connect everything is with delegation. I set a delegate property which calls back the asynchronous result to either a selector or a method signature. I can even create an optional or required interface on the delegate which will allow Xcode to assist me a little with implementing the delegate. To learn about using delegates in Objective-C I created this simple project.
http://www.smallsharptools.com/downloads/ObjC/Delegates.zip
It defines a Worker class which allows you to initialize the class with a delegate. When the work is done with the doWork method it looks for a method signature on the delegate to send a message back to it. It uses the following code.
if([[self delegate] respondsToSelector:@selector(workFinished:)]) {
NSString *msg = @"That's it? Easy!";
[[self delegate] workFinished:msg];
}
It looks for the workFinished: method to pass back a message. I declared this method signature as an optional interface with the following code in the header, Worker.h.
@protocol WorkerNotifications
@optional
- (void) workFinished: (NSString *) msg;
@end
You can see the rest of the project from the download for all of the details. But these 2 code snippets show how this delegation pattern works. But with the Twitter class I need to know the context of the method which started an asynchronous action which leads to a callback to a delegate method. If I call the sendUpdate method more than once from the calling class, how I am supposed to know the context of the callback?
Normally with a language like JavaScript, Java or C# I would create an inline closure or anonymous class which would have access to the starting context, but that is not possibly currently with Objective-C on the iPhone. I found that this question was already asked and answered on StackOverflow.
Anonymous delegate implementation in Objective-C?
So what I have done is skip the optional interface and instead passed in a selector which the Twitter class will call when the asynchronous action is completed. A call to start this action looks like...
CMTwitterEngine *engine = [[CMTwitterEngine alloc] initWithDelegate:self];
[engine setSendUpdateFinished:@selector(sendUpdateFinished:)];
[engine setSendUpdateFailed:@selector(sendUpdateFailed:)];
[engine setParsingSendUpdateFailed:@selector(parsingSendUpdateFailed:)];
[engine setUsername:TWITTER_USERNAME pass:TWITTER_PASSWORD];
[engine sendUpdate:statusUpdateText.text];
This code first initializes the engine reference with self as the delegate. To attach the callbacks I send in selectors which I originally had on the sendUpdate method signature but the method calls got pretty long. I opted to simply set properties of the selectors. This all works but I am not sure I like how this is working since it only partially solves my problem.
To complete this example, I finish the asynchronous work and eventually call a method internally which looks for the given selector and calls it if it is defined.
- (void)sendUpdateFinished:(NSDictionary *)dictionary {
if (self.sendUpdateFinished != nil) {
[self.delegate performSelector:self.sendUpdateFinished withObject:dictionary];
}
}
I can pass in the status message to send as a Twitter update but I still do not have the context of the originating call. What if I want to call sendUpdate more than once and the first asynchronous call is still running? And what if the second call finishes first? They will both have self as the delegate so I would have to either track the context somehow or pass them to a different selector to distinguish them, which also does not satisfy my needs. What happens if I have 3 or 4 or 5 asynchronous calls? I need to know which ones were sent successfully and when they are complete.
It appears the only way that I can do all this is to create a class which holds onto all of the properties needed for the context, have that class act as the delegate for the call to the asynchronous Twitter method and then report back to the parent class which is likely UIViewController. I would take this approach but I have not read about this approach or seen any sample code yet which does this.
What would you do? How would you handle multiple asynchronous calls going out which could end in a different order than going out and then process them with context upon completion?
发布评论
评论(3)
我认为你的情况是使用 NSNotificationCenter
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html
I think your situation is a great place to use NSNotificationCenter
http://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/Reference/Reference.html
我必须第二(或第三)之前在 NSNotificationCenter 中发布的答案,这可能就是您在这里寻找的内容。
本质上,当可能存在许多委托时,通常会使用通知,所有委托都需要执行某些操作来响应已发生的单个操作或事件。将其视为一对多的委托或观察者模式的实现。需要了解的基本内容是:
NSNotifications 有一个您定义的名称,它只是一个 NSString。通知可以按名称发布,对象可以注册以按名称接收通知。
当发布通知时,可以提供 notificationSender 对象和/或 userInfo 字典。 notificationSender 是在接收者处理给定通知时确定谁发布了给定通知的直接方法。 userInfo 是一个 NSDictionary,可用于提供附加的上下文信息以及通知。
因此,您不必强迫所有工作人员采用非正式协议并在运行时使用反射样式调用方法,只需使用 NSNotificationCenter 注册工作人员实例即可。通常,向 NSNotificationCenter 的注册是在每个工作类的 init 方法中完成的。然后,每种类型的工作人员的实例通常会在 NIB 中设置为“冻干”对象,或者可以在应用程序委托中以编程方式实例化,以便它们在应用程序生命周期的早期就在通知中心注册。
当事情发生时,您将 NSNotification 发布到 NSNotificationCenter(本质上是一个单例),然后已注册接收该特定类型通知的所有其他内容都将调用指定的方法来处理该通知通知类型。完成后,这些方法可以调用发送者的通用方法(通过 NSNotification 的对象方法获得)来告诉发送者他们已经完成了工作。
一旦每个已知的工作人员签入了发送者的通用方法,就可以继续执行要执行的任何工作人员完成后代码。
I have to second (or third) the previously posted answers in that NSNotificationCenter is probably what you're looking for here.
Essentially one typically uses notifications when there are potentially many delegates all of which need to do something in response to a single action or event that has occurred. Think of it as a one-to-many sort of delegation or an implementation of the observer pattern. The basic things to know are:
NSNotifications have a name that you define which is just an NSString. Notifications can be posted by name and objects register to receive notifications by name.
When a notification is posted a notificationSender object and/or userInfo dictionary can be provided. The notificationSender is the direct way of determining who posted a given notification when it is being handled by the receiver. The userInfo is an NSDictionary that can be used to provide additional context info along with the notification.
So, rather than forcing all of the workers to adopt to an informal protocol and messing around with reflection style calling-methods-at runtime you just register instances of the workers with NSNotificationCenter. Typically the registration with the NSNotificationCenter is done in an init method of each worker class. Instances of each type of worker are then typically set up as "freeze dried" objects in a NIB or can be programatically instantiated in the app delegate so that they get registered with the notification center early on in the app's life.
When the thing occurs you post a NSNotification to the NSNotificationCenter (which is essentially a singleton) and then everything else that has registered to receive that particular type of notification will have the method called that was specified to handle that type of notification. When done these methods can then call a common method back on the sender (obtained via NSNotification's object method) to tell the sender that they've completed their work.
Once each known worker has checked in the the common method on the sender can then go on to whatever post-worker-completion code is to be performed.
需要考虑的一件事是使用通知。简化代码,减少耦合。
One thing to consider is using Notifications instead. Simplifies code, couples things less tightly.