8

Cocoa uses delegates extensively to provide (among other things) callback methods for asynchronous operations. However, I personally hate the delegate model and how it pollutes the current class with handlers for very specific child operations. UIAlertView is a perfect example.

Therefore, I'm wondering if it's possible to simply create an anonymous delegate object that meets the requirements of a delegate protocol (such as UIAlertViewDelegate) via blocks and pass this anonymous object wherever a delegate reference is expected.

I imagine something like so:

id myDelegate = @{
    alertView:alertView didDismissWithButtonIndex:index = ^ {
        ...
    }
};

UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:... message:... delegate:myDelegate cancelButtonTitle:... otherButtonTitles:...;
[alertView show];

I've heard Objective-C has some good dynamic language features but I don't know how to simply add a method/selector to an object. Can this be done in a relatively straightforward manner?

devios1
  • 36,899
  • 45
  • 162
  • 260
  • 1
    There's a question about completely generic delegates from a month ago: http://stackoverflow.com/q/15438410/603977, and there's an older question specific to `UIAlertViewDegate` somewhere, but I can't find it at the moment. – jscs Apr 19 '13 at 00:25
  • 1
    You can, of course, create an entirely different object for the delegate. It doesn't have to be, eg, your ViewController. (But Objective-C blocks make my head explode, so I avoid them.) – Hot Licks Apr 19 '13 at 00:41
  • 1
    Hopefully Apple will add a blocks based API to thing like UIAlertView like they did for NSOpenPanel on the Mac side. – Jon Shier Apr 19 '13 at 03:20
  • Sorry, the other one I was thinking of was `UIActionSheet`, not `UIAlertView`: [Attaching an object to a UIActionSheet](http://stackoverflow.com/q/12763066). – jscs Apr 19 '13 at 19:10

4 Answers4

5

Yes, the language features you mention are exposed via the objective-c runtime, although there's no built-in facility to dynamically create delegate classes and the runtime api is not the friendliest of things.

There is a library called A2DynamicDelegate, which does exactly what you're talking about. I haven't used it, but it may be worth investigating.

EDIT: A problem with this approach is that delegates are not retained, so you'll need to either keep a strong reference somewhere else, or an add an associative reference to the UIAlertView. You may find that all this fuss isn't worth it and just adding an extra method to your view controller works better (you can conform to the delegate protocol in a class extension to avoid polluting your interface).

Chris Devereux
  • 5,453
  • 1
  • 26
  • 32
  • and here is [modern implementation in BlocksKit](https://github.com/pandamonia/BlocksKit/blob/master/BlocksKit/A2DynamicDelegate.h). – Cfr Jun 24 '13 at 11:04
3

One option is to write a class which wraps your blocks for the individual methods of the delegate protocol into a delegate object. For details see this answer.

Community
  • 1
  • 1
CRD
  • 52,522
  • 5
  • 70
  • 86
  • While writing a separate class to wrap the blocks would help with the separation of concerns, it's still an awful lot of overhead/boilerplate for something that should otherwise be pretty simple. – devios1 Apr 19 '13 at 18:20
2

You should consider using a category that adds block support to UIAlertView, which seems to address your use case. UIAlertView-Blocks is my favorite one although there are many others.

jszumski
  • 7,430
  • 11
  • 40
  • 53
0

If you want to use blocks with a delegate-based API, you'll have to do some subclassing. For example, see PSAlertView, which subclasses UIAlertView to provide a block-based API.

fumoboy007
  • 5,345
  • 4
  • 32
  • 49
  • Subclassing isn't ideal. :/ – devios1 Apr 19 '13 at 00:33
  • 3
    it is not only not ideal, it is forbidden: [The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.](http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html) – vikingosegundo Apr 19 '13 at 01:18
  • 2
    You don't need to subclass `UIAlertView` in order to add a block-based API. You could, for example, make a `MyAlert` class which owns an alert view, sets itself as the delegate, and responds to delegate methods by invoking blocks supplied by client code. – rickster Apr 19 '13 at 06:20
  • 1
    @rickster That's similar to what [BlocksKit](https://github.com/zwaldowski/BlocksKit) does, which works quite well. – Aaron Brager Apr 19 '13 at 14:31
  • @rickster Exactly. I am using similar approach and the code is much cleaner immediately. – Sulthan Apr 19 '13 at 14:32
  • @vikingosegundo We're not changing any views; we're just adding a block-based API. – fumoboy007 Apr 19 '13 at 23:27
  • PSAlertView is subclassing. And that is forbidden explicitly. If that leads to refusal is up to the reviewer. – vikingosegundo Apr 19 '13 at 23:42