In many cases, directly-related pieces should probably have a tight understanding of what's happening with their neighbor or ancestor or descendant, and delegates are great for this. But sometimes, I want my software to announce that certain things have happened, or completed, or whatever, and not care who, if anyone, is listening, and NSNotificationCenter is brilliant for this. For instance, a top-level UI feature like an indeterminate progress indicator doesn't need to know of every single bit of work being performed by lower levels of the software, and tightly coupling them - even along a delegate chain of some sort - is fraught with potential edge cases. Simply listening for start and end announcements means dozens of not hundreds of lines of code eliminated, and maintaining a crazy delegate chain unnecessary.
In any case, I've found this pattern extremely helpful and useful, and is one of my favorite bits of working with Objective-C (and now Swift).
Delegate is probably the most fool proof way to do it and is pretty much protected by the compiler to give you the warnings you might need and doesn't require much template. But ultimately there are a lot of things where it's frankly a one to many scenario and delegation is out of the picture.
I suppose I've continually grappled fixing both bugs in KVO and NSNotification, and it seems like Notifications are much easier to find by just searching for the const string to audit the observers. KVO is tricker especially if there are keypaths and what if the name of the property is changed? The whole thing happens in the runtime so it's basically a nightmare.
So ultimately yeah, you can clean up KVO and make a better implementation. I've written something that makes a intermediary object that managed some of the trickier aspects of KVO (having to call super and pass a context and dealloc etc). But now I'm at a new place and I wouldn't put the effort into getting everyone on board into using that code instead of the usual Foundation stuff.