mikeash.com pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html commentshttp://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsmikeash.com Recent CommentsThu, 28 Mar 2024 17:58:57 GMTPyRSS2Gen-1.0.0http://blogs.law.harvard.edu/tech/rssDavid Baraff - 2016-01-24 19:23:07http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsA dispatch queue per object? Is that an issue? <br /> <br />I asked about this here: <br /> <br /><a href="http://stackoverflow.com/questions/34980308/how-many-is-too-many-for-create-dispatch-queues-in-gcd-grand-central-dispatch">http://stackoverflow.com/questions/34980308/how-many-is-too-many-for-create-dispatch-queues-in-gcd-grand-central-dispatch</a> <br />e7cad373a4a9f4df9c7e3f4c51df9586Sun, 24 Jan 2016 19:23:07 GMTMobiledic - 2015-03-04 14:42:50http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsAlthough I am not quite familiar with Swift(I still write code in OC), the explanation in the article is really throughout. Maybe it's the time to switch to Swiftb578bb0f68d115fe73803edad728c4f9Wed, 04 Mar 2015 14:42:50 GMTmikeash - 2015-02-15 02:34:48http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsThat type is a function that takes an <code>AnyObject</code> and returns another function. The return type takes <code>Parameters</code> and returns Void6aa120587e83186546f12dd0b04aa3a7Sun, 15 Feb 2015 02:34:48 GMTGaurav Agarwal - 2015-02-13 02:46:34http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsCan you please put some light on this syntax: <br /> <br /><code> private let f: AnyObject -&gt; Parameters -&gt; Void </code><code></code>4e875096d32350f034383b5dc2d79df9Fri, 13 Feb 2015 02:46:34 GMTmikeash - 2015-02-06 16:32:20http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>Guillaume Lessard:</b> Coincidentally I discuss the pros and cons of that in my latest article. You're right that a spinlock would work pretty well here, but I prefer not to use them without some identified speed problem. <br /> <br /><b>Deniska:</b> I'd say that indicates a design problem in the Core Data API. If save notifications affect everything related to a particular persistent store coordinator, then that should be the object posting the notifications, not the individual contexts. (If there are cases where you only care about the specific context then I'd say both should be sending a notification.)66f31aa3192824c22d97a6338c395db2Fri, 06 Feb 2015 16:32:20 GMTDeniska - 2015-02-06 16:09:03http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsMike, <br /> <br />Actually, that approach works okay with multiple core data stores configuration. <br />Notification has sender which is a context being saved. I check that my context and sender have the same persistentStoreCoordinator. <br />If not - I don't need to merge changes. <br />Something like that. <br />And moreover, this works faster (in my case) than having several nested contexts. 69c3a11d3a831a8eb56a8225a693c9deFri, 06 Feb 2015 16:09:03 GMTGuillaume Lessard - 2015-02-02 20:06:09http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsVery interesting article, thanks. <br />Regarding the use of dispatch_sync to a serial queue, if you've ever benchmarked that particular construct you'll know that it is frightfully slow. You could instead use OSSpinLock, which would be just fine for the particular application you're using: <br /> <br /><code> <br />private var lock = OS_SPINLOCK_INIT <br />private func synchronized(f: Void -&gt; Void) { <br />&nbsp;&nbsp;OSSpinLockLock(&amp;lock) <br />&nbsp;&nbsp;f() <br />&nbsp;&nbsp;OSSpinLockUnlock(&amp;lock) <br />} <br /></code> <br /> <br />The time delay involved can be orders of magnitude shorter this way than when using dispatch_sync.893c8201266a23a3760524b5750107bcMon, 02 Feb 2015 20:06:09 GMTRupert - 2015-01-29 10:56:52http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsHi Mike - thanks for another informative article! I've always found your articles very instructive, particularly the ones on the low-level performance of Swift. <br /> <br />I completely agree with you on the evils of singletons, of which nil uses of <code>NSNotificationCenter</code> are certainly one. It might be possible to argue that if we are using a non-shared instance of the notification centre (ie one that we instantiated and injected everywhere it is used to subscribe / publish) then it is no longer a singleton but merely a stateful object with dynamic properties. However, almost all uses of it seem to pass through the shared instance. <br /> <br />Unfortunately there are several scenarios where it seems that we have no choice but to use this singleton behaviour, such as when listening for keyboard notifications (in iOS) because the objects we want to listen to are not made available to us (if anyone knows a way around this I would be interested to hear it). <br /> <br />I have been taking great pains to get rid of all singletons in my code (to the length of injecting <code>NSBundle</code> and <code>UIScreen</code> into objects which need access to them, instead of calling <code>mainBundle</code> and <code>mainScreen</code>). Might I suggest an article on the best ways to avoid singletons in Cocoa? <br /> <br />For those interested, the most persuasive argument against singletons that I came across is this old article - <a href="http://www.object-oriented-security.org/lets-argue/singletons">http://www.object-oriented-security.org/lets-argue/singletons</a> <br />It seems that so many code/application security problems would simply not exist if complete dependency injection were enforced. <br /> <br />Thanks again.a1cae770824099c8fa836ccbd8352084Thu, 29 Jan 2015 10:56:52 GMTmikeash - 2015-01-28 01:30:01http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>Pavel:</b> It does make a copy. Swift arrays have value semantics, so just doing <code>a = b</code> gets you a new one. <br /> <br /><b>Bill:</b> Consider a generic passthrough function: <br /> <br /><code>func call&lt;T&gt;(function: T -&gt; Void, parameters: T) { <br />&nbsp;&nbsp;&nbsp;&nbsp;function(parameters) <br />}</code> <br /> <br />This works for multiple parameters. Now imagine if you wanted to write a variant that would, say, add 1 any integer parameters before calling the original function. There's no reasonable way to accomplish that.dfc3023653b8200020060db5060fd973Wed, 28 Jan 2015 01:30:01 GMTBill - 2015-01-27 13:28:44http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsThis is a really nice API! <br /> <br />Can you explain what you mean by "(for example, it's essentially impossible to modify any of the parameters)" in the section about using named tuples as the argument to a single-parameter function?56ce05edcf1c28c814110c333687a03dTue, 27 Jan 2015 13:28:44 GMTPavel - 2015-01-27 07:45:54http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>Mikeash</b> I still don't get why you get a local reference to self.entries in description, is that supposed to make a copy instead of a reference?ba6d254e238038b2c23289b0e99c5db7Tue, 27 Jan 2015 07:45:54 GMTR. George - 2015-01-26 22:05:47http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsI use the same Core Data pattern that Deniska describes, to very good effect. <br /> <br />The first thing in the NSManagedObjectContextDidSaveNotification handler method is a test of the posting context to see if it's one I care about: "Is this save affecting my store?" <br /> <br />Given the extensive creation of scratch contexts all through the app, it's simpler than registering a listener for each context as I create it, and deregistering as I discard.b9849456f6cd4ef28e739eb7ddaf0d82Mon, 26 Jan 2015 22:05:47 GMTmikeash - 2015-01-25 02:31:41http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>David:</b> No worries, I can handle it. Sometimes I have strong beliefs and I'm not shy about telling people. I don't expect everybody to agree though. <br /> <br /><b>Jordan:</b> Nice suggestion about using _ instead of ignored. I think I'm going to stick with "ignored" for now as it seems more clear to me. This may just be due to the newness of the language, though. Once people have used Swift for a while I imagine that _ will look more natural there. <br /> <br /><b>Pavel:</b> Temporary brain malfunction. I pushed a quick fix. Thanks for pointing that out! <br /> <br /><b>Deniska:</b> Doesn't that assume that your app is only ever manipulating one Core Data store? That seems like a bad assumption to make. <br /> <br /><b>Jason:</b> Excellent question. It comes down to not being able to figure out a good way to have a heterogeneous collection with generics. In isolation, it would make sense for <code>ObserverSetEntry</code> to be written as <code>ObserverSetEntry&lt;T, Parameters&gt;</code>. That way, everything would be consistently genericized and life would be good. But I couldn't figure out a way to have <code>ObserverSet</code> reasonably contain and talk to a bunch of <code>ObserverSetEntry</code> objects with different <code>T</code> types. By having them all use <code>AnyObject</code> that problem is bypassed. <br /> <br /><b>Jean-Daniel:</b> I agree, that looks like a good case for <b>nil</b>. In this particular case, since you'd likely never want to listen for that notification for anything <i>but</i> <code>nil</code>, the same functionality could be provided with a global <code>ObserverSet</code> instance for all of <code>NSBundle</code>. Loaded code is inherently global, of course, and it would make sense to have an explicitly global notification system to go with it.00a7e84c29ae0a4c83402c0714fb8020Sun, 25 Jan 2015 02:31:41 GMTJean-Daniel - 2015-01-24 21:55:12http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsA useful use case for anonymous notifications is tracking bundles dynamically loaded by registering for NSBundleDidLoadNotification.767a36217c63e4533997de138d210f31Sat, 24 Jan 2015 21:55:12 GMTJason - 2015-01-24 18:02:15http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsGreat post! A question for you: <br /> <br />You mention how it's unfortunate to bypass swifts type safety when you go from AnyObject to T when adding an entry. Why must you use AnyObject in one place and T : AnyObject in the other? Can you please explain the reasoning behind that? (Sorry if this is obvious, I'm a swift beginner). <br /> <br />Also, another reason to avoid anonymous notifications: testing becomes a huge pain since there may be multiple instances of those objects you thought were singletons while all those tests are running. I guess that's also a reason to avoid singletons. a8f8f4ad89ff1ee0296ed6db64b9bfb0Sat, 24 Jan 2015 18:02:15 GMTDeniska - 2015-01-24 09:24:39http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsOne of the legitimate uses of nil-objects could be listening to NSManagedObjectContextDidSaveNotification. <br />App could have multiple contexts for internal needs and main thread could be integrating changes of data into main context. And main context should not be aware of all possible background contexts. So, we listen to all notifications and merge those changes. 8d6f0b0c11bf84c19d7eaa52e07a6ab3Sat, 24 Jan 2015 09:24:39 GMTPavel - 2015-01-24 07:43:42http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsWhy do you get a reference to self.entries in description() and then continue using self.entries? If that is not a typo, can you explain the reasoning behind it.420bfccc51d2dac9138899da08d319a0Sat, 24 Jan 2015 07:43:42 GMTJordan - 2015-01-24 04:48:14http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsNicely done. Suggestion: use "_" instead of "ignored". <br /> <br />Jon: I agree with Mike, and Apple's classes definitely do this. It's like listening to UITableViewSelectionDidChangeNotification on &lt;em&gt;all UITableViews&lt;/em&gt; in the app. You can do it, but it only makes sense if you know that all the UITableViews in your app should behave the same way. The same goes for any notifications you create yourself: if everything in the app deserves the same response, then it's great...but it makes it harder if you ever want to differentiate in the future.1b958f1b6d102cb9dd7bca12063e42cbSat, 24 Jan 2015 04:48:14 GMTDavid - 2015-01-24 03:34:24http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsKeep writing, though! Sorry, I didn't mean to be discouraging myself. Big fan, just disappointed by the attitude.3bec90a698bd51ef4b10bea543d7d777Sat, 24 Jan 2015 03:34:24 GMTmikeash - 2015-01-24 02:43:58http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>Jon:</b> What I'm saying is that there are potentially a lot of instances in your program that could be sending a notification, and the appropriate action for an observer depends on which instance it is. For your logout notification example, again, you might want to delete credentials, but *only* when that's sent by the login controller you actually care about. Another login controller used by a different part of the program should not cause that to happen. It sounds to me like you're relying way too much on there only being one instance of any given class. <br /> <br /><b>David:</b> If you expected a humble approach here then I think you may be confused. You'll get zero traction with me by proposing that I shouldn't be critical of what I see as Cocoa design mistakes. Cocoa has suffered from some mistakes during its long life, and I'm not going to be shy about pointing them out. More than that, a lot of Cocoa programmers value expediency over good design. For example, I see a ton of Cocoa apps that are chock full of singleton objects that shouldn't be there, and are used just because it's slightly easier to access a singleton than to pass instances around. Anonymous notifications look to be the same sort of problem to me. You certainly have every right to disagree, but you need to bring some sort of argument if you want to discuss it, not simply declare me to be wrong because I might disagree with Apple.e36bb37e043c396c520a1fa3f87cc35eSat, 24 Jan 2015 02:43:58 GMTDavid - 2015-01-24 02:19:17http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsI agree with Jon. NSNotificationCenter is useful, partly, because it does allow anonymous notifications. I believe that's a big part of what it was invented to do! <br /> <br />The idea that you could somehow end up with multiple instances that would all process a logout event, and clobber each other's efforts, seems like a stretch. People who want to use anonymous notifications are likely to be mindful of what they're doing, and it's wrong to suggest that they should feel insecure for correctly employing bona fide Cocoa features. <br /> <br />From a blog called NSBlog, I would have expected a more humble and encouraging approach. Attempting to position oneself as superior to the creators of Cocoa is asinine.cf3dce2b1b521066b6a90af8d14059e2Sat, 24 Jan 2015 02:19:17 GMTJon - 2015-01-23 23:01:13http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsI'm not sure why you're saying I have to be aware of all objects that send a notification (if you mean I need a reference), since that's exactly the scenario I avoid when using anonymous notifications. Some object posts a "Logout" notification, I don't really care what it is. Some other object or objects receive that notification and take some action. Or perhaps no objects have yet been created that listen for that notification. At most I have to be aware of what notifications could be sent from a class, if I care, and that's just a documentation issue. I can't be bitten by notifications I don't know about. <br /> <br />Or did you mean that multiple classes could send the same notification for different reasons and I might not know about it? i.e. Some random object could broadcast a notification I listen for I wasn't expecting. That just seems like bad notification usage by the offending object. Apple's classes don't do that (notifications are unique to a class or framework, usually). If a third party library was, say, issuing the UIKeyboard notifications for some reason, I'd probably rethink using that framework, if I could. <br /> <br />If there are really times that I don't want to take action based on a notification, I rely on the info dictionary to determine what action to take. In my experience, such situations are rare, especially for notifications that are intended to be global. If the notification doesn't include a useful info dict, again, that seems like bad notification design, not a problem with the anonymous notification pattern in general. <br /> <br />My only point is that your solution, while interesting, completely ignores this valid and useful scenario for notifications.46d442a8d641cf2b059e57bd7e0cad9bFri, 23 Jan 2015 23:01:13 GMTmikeash - 2015-01-23 22:22:22http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsStuff will build up if you never send notifications, since dead entries are only cleaned when notifying. That would easily be remedied with a check when adding as well, if needed.b8f19922f141e20b6da8d81d8e9c8581Fri, 23 Jan 2015 22:22:22 GMTbob - 2015-01-23 22:06:10http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsAlthough deallocated observers are no longer called, doesn't this have the problem of still leaving the ObserverSetEntry object (with nil object) in the observer set forever? So if you regularly register observers, and they deallocate, the array will steadily build up with junk.9e283f90c96ed0893879e84b4d72d817Fri, 23 Jan 2015 22:06:10 GMTmikeash - 2015-01-23 21:55:18http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#comments<b>Jon:</b> If you register for a certain notification sent by all objects, now your code has to be aware of all objects in your app that send that notification, even if they're being managed by a completely different instance. For your logout event example, you wouldn't want to clear caches or delete files if some other instance's session logged out, so you're effectively limiting yourself to one session at a time. I think it just comes down to your statement that "I don't consider singletons an inherently bad thing." If you're comfortable limiting things to one instance even when it's not strictly required then that concern goes away, but I'm not, myself. <br /> <br />I'm a bit puzzled about your "manually deregistering" comment, since the code above does handle exactly that with no fuss. <br /> <br /><b>Brian B.:</b> Adding a notification observation to each object as you add it to the array, and removing it when you remove from the array, seems just as easy and no more bug-prone. Unless you add and remove from many different places, which you probably shouldn't be doing anyway.1e4c05d5f337aee8b2b875ea2ab1b68aFri, 23 Jan 2015 21:55:18 GMTBrian B. - 2015-01-23 21:25:58http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsNo legitimate use for catch-all? <br /> <br />I use that all the time. E.g. when managing a collection of some objects that all post notifications you are interested in and that collection changes over time. <br /> <br />With catch all you register a single notification to watch for posts from any object in [init], then in your handler method: <br /> <br />if ([collection containsObject:note.object]) { <br />handle notification... <br />} <br /> <br />and in [dealloc] you deregister <br /> <br />This would messier and possibly more bug prone without catch-all -- even with the Swift version.be76e486ece7c1377727f2927e5a7fcdFri, 23 Jan 2015 21:25:58 GMTJon - 2015-01-23 21:25:25http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsWhy would that effectively make them singletons? Objects register and deregister for various notifications as they come and go. At least when using NSNotificationCenter, registering for events that are never broadcast is just fine. For non singleton objects I mostly use NSNotificationCenter for system events. But since I don't consider singletons an inherently bad thing, I also use them as communication mechanism for the few singleton objects I usually have. Stuff like logout and login events to tell various singletons to do something (i.e. clear caches, reset networking, write to disk, delete files). NSNotificationCenter also serves as a central sort of service for notifications, so I don't have to know exactly what objects I want to see certain events for in the first place. <br /> <br />Also, if you're redoing the observer/notification mechanism anyway, why not tackle the most annoying aspect of using them, manually deregistering them. I've seen solutions for that in Objective-C but a Swift implementation would be interesting, if possible.5251a723ec697d9aeb4e99a207296d0dFri, 23 Jan 2015 21:25:25 GMTmikeash - 2015-01-23 21:08:57http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsHow do you use nil-targeted notifications without effectively turning those parts of your application into singletons?49975891917f21cc059a8a285d46ab37Fri, 23 Jan 2015 21:08:57 GMTJon - 2015-01-23 20:25:11http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsIn fact, I can't think of an instance where I've seen or used NSNotificationCenter for anything other than anonymous notification broadcast and reception. If I knew the object I wanted to watch I would just use an observer.33deb7c63a0aeffbfe4d2a4f0f86bf0eFri, 23 Jan 2015 20:25:11 GMTJon - 2015-01-23 20:22:36http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsThis isn't really the same as using NSNotifications/Center. NSNotifications can be anonymous and these are not, so you're missing the biggest difference between notifications and observers. This is an interesting implementation of observers in Swift but it's clearly not a replacement for NSNotificationCenter. f634e4ebb2e899ca4e385245e6685f61Fri, 23 Jan 2015 20:22:36 GMTmikeash - 2015-01-23 17:41:33http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsI'm not sure if I've ever seen a legitimate use of a nil object. The catch-all facilities of NSNotificationCenter are interesting, but are hard to use without making dangerous assumptions about the global structure of the process you're living in. <br /> <br />As for dynamicType, you can certainly use the class name instead. I decided not to simply because that ends up being repetitive and requires renaming a bunch of extra spots if you change the class name. Not a particularly big deal, more a matter of personal preference.8ddeecd4c7919e1f1ca3ebb987b20b82Fri, 23 Jan 2015 17:41:33 GMTJacob Gorban - 2015-01-23 17:34:16http://www.mikeash.com/?page=pyblog/friday-qa-2015-01-23-lets-build-swift-notifications.html#commentsI developed something similar for my code (called it Channels). It has somewhat simpler implementation (storing direct functions in the arrays and not curried method functions like what you create. <br /> <br />Of course, in this case I can't unsubscribe to the channels but so far it works for my use-cases. <br /> <br />Regarding your concept of this replacing the <code>NSNotificationCenter</code> notifications, there is one important use-case that not covered in this approach, and seems not possible indeed, is observing a notification on any object that sends it (nil for object in addObserver). In a way this is also what allows such loose coupling on <code>NSNotifications</code>. <br /> <br />A question that I do have, is why do you have to go through <code>dynamicType</code> property? You could use ClassName.method, if I'm not mistaken, too. <br />7d6d6a1567e53b2fa5eb3374132ac5aaFri, 23 Jan 2015 17:34:16 GMT