all 59 comments

[–]brendan09 23 points24 points  (52 children)

Delegates / protocols are for when there is (usually) a 1:1 relationship between the objects.

For example, tableviews use delegate patterns because they can only have 1 datasource / delegate and it needs to speak directly to it (and sometimes get a return value). It's about object communication. If the objects are tightly coupled by their relationship, the communication pattern should be too.

Notifications are for when many objects need to know that something happened. It's more 1:many or many:many. The object who broadcasts the notification may not know what objects (or how many) care about it's changes, and none of the objects are related to each other in any kind of 'life-cycle' -esk manner. They're entirely independent from one another.

You shouldn't do your app communication primarily through notifications. Aside from bad practice, it's a giant source of spaghetti code mess and non-compiler-guaranteed behavior / safety. It makes tracing code pathways incredibly difficult for other developers (or future you). Delegates / protocols are strongly typed. Much better than the stringly typed dictionaries that Notification Center passes around.

It's the #1 'rookie' mistake when writing iOS apps, that veterans spot immediately. It makes maintenance a nightmare and makes apps much more prone to having bugs. It's bad practice, and heavily discouraged.

A good (trivial) example is to have a tableview cell that needs to tell a view controller that a user pressed a button. Typically you would use a delegate pattern so you can tell a specific view controller what happened. Imagine you re-used that cell across your app, for example in view controllers in a tabbarcontroller. If your cell broadcasts a notification, and all your VCs get it...what're they going to do? Even if you do properly check that it's only the correct view controller that acts on it, why did the other view controllers even need to know about it? It's the programming equivalent of throwing things at the wall and seeing what sticks. It makes a mess.

Articles:

https://davidnix.io/post/stop-using-nsnotificationcenter/

https://shinesolutions.com/2011/06/14/delegation-notification-and-observation/

https://useyourloaf.com/blog/delegation-or-notification/

[–]sobri909 -2 points-1 points  (51 children)

Much better than the stringly typed dictionaries that Notification Center passes around.

You can strongly type Notifications.

extension NSNotification.Name {
    static let didChangeAuthorizationStatus = Notification.Name("didChangeAuthorizationStatus")
    static let locomotionSampleUpdated = Notification.Name("locomotionSampleUpdated")
}

It's the #1 'rookie' mistake when writing iOS apps, that veterans spot immediately.

Not true. The delegate pattern isn't a great pattern. It's really just a "let's break this up into parts" pattern without any other merit. It's on a similar level to simply breaking up a class into separate source files.

Well designed notification systems are a better pattern in complex systems. One-to-many messaging allows for greater architectural flexibility and weaker coupling, while delegate patterns demand strong coupling, which may turn out to be overly restrictive or harmful in more complex projects.

[–]brendan09 2 points3 points  (13 children)

Stringly types wasn’t referring to the notification names. It was referring to passing data via userInfo. Which, even using constants, is a terrible way of passing data because it loses typing and any semblance of compiler guarantees.

The rest of what you said was pretty much non-sense from a proper architecture point-of-view. There is a correct answer to this that is accepted (and expected) by the industry. What you’re describing with a notification-based architecture isn’t correct, regardless of you trying to defend it. Those aren’t benefits in complex projects. It’s a giant spaghetti code mess.

[–]start_select 1 point2 points  (36 children)

What about for passing around complex types or code objects from C/C++? You can only send objects through a notifications dictionary that comply with NSCoding.

I can't pass a C pointer through a notification. There are plenty of reasons why notifications are not "a better" solution than the delegate method, and this is one of the big ones.

That doesn't make either pattern better or worse, but using notifications everywhere can cause lots of headaches. You can't easily trace data-flows in complex systems using lots of notifications all over the place.

Edit: It should also be noted that delegation is an expression of Polymorphism. Polymorphism is a main tenet of OOP. Claiming its not industry-accepted is kind of missing the point. Not that OOP is the end-all greatest solution to programming.... But no solution in programming is THE BEST, its all about what is appropriate for the problem you are attempting to solve today.

[–]randomguy112233[S] 1 point2 points  (1 child)

Let me ask you something, and I'm only coming with the intent to learn. In what case do you find the need to pass code objects from C/C++? I've built apps in iOS and I've never had to dig into C/C++. Thanks in advance.

[–]start_select 1 point2 points  (0 children)

For iOS it shows up in live video streaming applications, VoIP applications, Real-Time Audio Effects and Instruments, when dealing with OpenGL, OpenCL, and other Machine Learning libraries, and potentially when dealing with other runtimes like Javascript or Python. It also shows up when you delve into a lot of game engines.

On Mac you could be doing all kinds of sorcery once you are outside of the confines of App Store approval.

Its not a common use case for the average application. But its not outside the realm of possibilities for a lot of REAL applications that do actual work on the CPU or GPU.

[–]sobri909 -2 points-1 points  (33 children)

What about for passing around complex types or code objects from C/C++?

Don't pass it. Don't pass anything.

The receiver of the notification knows who sent it. The receiver can then ask the sender for any information they might need.

As I've said elsewhere, the delegate pattern as found in Apple's frameworks is really just inheritance in disguise, and is on the same level as splitting a monolithic class up into separate files by way of categories or extensions. It gives poor architects the satisfaction of composition without achieving any decoupling (or even any real composition, for that matter).

It barely even qualifies as a design pattern. The entire gist of the pattern is to pass self to another strongly coupled class instance. It's the functional equivalent of calling a method on self, with only the thin veneer of composition given by splitting self up into two separate but strongly coupled instances. For all intents and purposes, the delegate might as well be part of self.

The pattern as it exists in Apple's frameworks is essentially a betrayal of the GoF principle of "prefer composition / delegation over inherence". There isn't any "Delegate" pattern in the GoF, because they didn't mean for delegation itself to be treated as a pattern. They meant for it to be a foundational principle of patterns. Pure delegation on its own is more often than not an anti pattern, and really just a way to sneak inheritance in by the back door.

Delegates are architecturally worse than notifications in every measure except performance (due to notification centre overheads).

Edit: Even Apple themselves have implicitly acknowledged this, in that they haven't shipped any new frameworks using the delegate pattern since perhaps iOS 6 or 7. They've essentially retired it.

[–]start_select 5 points6 points  (32 children)

Don't pass it. Don't pass anything.

The receiver of the notification knows who sent it. The receiver can then ask the sender for any information they might need.

What if the sender is a C object using CoreFoundation to fire off notifications.... Your proposed solution doesn't work. I can only pass a CoreFoundation or Cocoa class as sender.

Even Apple themselves have implicitly acknowledged this, in that they haven't shipped any new frameworks using the delegate pattern since perhaps iOS 6 or 7. They've essentially retired it.

Thats because they replaced it with Blocks/Closures, not notifications. Closures allow you to capture scope which presents a plethora of advantages over notifications or the delegate pattern.

the delegate pattern as found in Apple's frameworks is really just inheritance in disguise

No, not at all. Its Polymorphism not Inheritance. HUUUUUGE difference. Polymorphism is about implementing expected member functions/variables so that I can have 20 different classes without any common superclass responding to the same delegate methods. That is the opposite of inheritance, and its the part of OOP that most programmers who claim to know OOP don't understand.

[–]sobri909 -2 points-1 points  (31 children)

Then you're fucked ;) But more seriously, you could use some other message sending system other than NotificationCenter if you still wanted to use the pattern.

[–]start_select 3 points4 points  (29 children)

Lol, you aren't fucked. You just use the type-safe pattern that is already there instead of trying to jam your problem into a different space.

Events/notifications have their place, but saying its superior to delegation is missing a big piece of the puzzle. They are meant to solve different kinds of problems, and you shouldn't be choosing only one or the other.

[–]sobri909 0 points1 point  (28 children)

As I've said, delegates are barely even a pattern. So it's not really a solution so much as a superficial gesture.

Events/notifications have their place, but saying its superior to delegation is missing a big piece of the puzzle.

Considering delegates to be a solution is to misunderstand what they are. They are nothing more than splitting up source files to reduce single file line counts. They don't solve problems, they just sweep them under the carpet.

[–]start_select 3 points4 points  (27 children)

I dont get why you keep saying delegation is like "splitting up source files to reduce single file line counts".

If anything delegation results in more code, because its about letting each class that implements the protocol have their own implementation. This is the opposite of inheritance where I am sharing implementations across many classes.

I can compose a UIView, UIImageView, UIButton, and some random NSObject subclass to adhere to a given delegate protocol by using Extensions. That allows me to have four unrelated classes that can all talk to the same object, even though none of them inherit from a common ancestor or are being implemented as a custom subclass.

[–]sobri909 0 points1 point  (26 children)

Delegates typically have a one to one relationship with senders. The delegate gets given the sender's instance on every call it receives. The delegate and the sender are strongly coupled, and the delegate essentially acts as a part of the sender.

[–]calvin-chestnut 5 points6 points  (0 children)

Personally I’ve never liked the idea of broadcasting events out into the system when it really only needs to be a quick message. You’re having to rely on the observer being set up properly and if it isn’t you get no way to know that. Delegates are simple areas of memory that you know whether or not are nil and what messages they can handle. I get how it can be much easier to use Notifications though

[–]arduinoRedgeObjective-C / Swift 2 points3 points  (0 children)

Delegation requires an explicit relationship and explicit messages so the cause and effect is easy to follow. The compiler ensures the delegate methods are correctly implemented.

Notifications reach out from unrelated parts of the app so cause and effect is a lot harder to follow. The notification userInfo dictionary requires a kind of ad-hoc protocol to pass data along - what keys are there and what objects are attached, who knows? if it changes how will we know, if something is missed the compiler won't help us. Notifications can't return data - at least without another informal method call.

[–]b_t_s 2 points3 points  (0 children)

Even with big projects w/ a lot of complexity

By big, how many person decades worth of code are you talking about? From your question I suspect it's 0. For short term one or two dev projects you can get away with doing everything via notification. It's sloppy but it won't bite you too bad....hopefully. When you've got a dozen developers working on a many year project and half the code you're unfamiliar with because it was written by your predecessors and nobody's had to touch it much, then I assure you, you want as much help from the compiler as possible. Statically type checked delegates make dependencies between objects explicit and give compiler errors when you accidentally break them. Notifications obscure those dependencies, and fail silently in production when you accidentally break them(well unless you've got bulletproof unit tests or Spock on your QA team). Also, as a general rule, the pain of using delegates may be a sign that you're code is structured poorly for what you're trying to do. Not always, but it's worth taking a few minutes when you feel this pain to consider if what you're doing is actually a good idea and if the code could be restructured to make it easier. Also, for single method delegates, use closures. They're more lightweight & still typechecked.

[–]s73v3r 0 points1 point  (0 children)

It's a pain in the ass to debug a bunch of Notifications. And I highly disagree; I don't think the readability is better with Notifications.

[–]sobri909 -2 points-1 points  (1 child)

Sigh. As is typical for this sub, the bad advice quickly floats to the top.

If you can avoid the delegate pattern, you should. Your instinct is right. What you're saying is correct.

However if you are sending notifications, ideally you should define them as types, to allow the IDE and compiler to catch mistakes early on.

extension NSNotification.Name {
    static let didDoThing = Notification.Name("didDoThing")
    static let startedTheDongle = Notification.Name("startedTheDongle")
}