[Objective-C] Cocoa's Target-Action Pattern

转载 2015年07月10日 16:26:43

转自:http://justinvoss.com/2011/08/19/cocoa-target-action/


Despite being a thin wrapper on top of C, Objective-C has dynamic features that make it feel more like a peer to Ruby than to C++. In this short article, I’ll introduce one of those features and show you how to use it in your code to reduce coupling and increase reuse.

Selectors

A selector is essentially the name of an Objective-C method, realized as an object in code. It has the type SEL, which is a primitive (no memory management needed). The actual contents of the selector are opaque, but you can get one from a method name with the @selector() syntax.

SEL setToolbar = @selector(setToolbarItems:animated:);

You can also get a selector from an NSString. This is handy if you want to store the selector in a config file.

SEL setToolbar = NSSelectorFromString(@"setToolbarItems:animated:");

Using Selectors

Once you have the selector, there’s a few things you can do with it.

You can ask an object if it responds to that selector; this is like asking if the object implements this method. If you’ve ever done reflection in Java, prepare for a breath of fresh air!

if ([controller respondsToSelector:@selector(setToolbarItems:animated:)]) {
  /* it's possible to set toolbar items on this controller */
}

You can ask an object to perform that selector:

// direct
[controller viewDidLoad];

// indirect
SEL action = @selector(viewDidLoad);
[controller performSelector:action];

This is the same as calling the method directly, but because the selector could come from a variable, it’s possible to change the method at runtime.

For example, UIBarButtonItem uses a target and action to call your code when the button is tapped. You might have some code in your view controller like this:

- (void)viewDidLoad
{
  
  button = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                            style:UIBarButtonItemStyleBordered
                                           target:self
                                           action:@selector(doneButtonHit:)];
  // more code
}

- (void)doneButtonHit:(id)sender
{
  NSLog(@"The done button was tapped!");
}

When you run the app and tap on the button, you’ll see “The done button was tapped!” in the console. It’s as if the button has a line of code like [controller doneButtonHit:self], but obviously it doesn’t: the button is a generic object that you can use off-the-shelf. The secret sauce is performSelector!

This pattern, called “target-action”, is used throughout Cocoa, especially in user interface code. It allows the UI widgets to stay generic, while making it easy to integrate your custom controller without needing to subclass anything.

Implementing Target-Action

Let’s write a really simple object that implements the target-action pattern. We’ll call it a button, but we’ll skip writing any view-related code. For simplicity, let’s assume that when the user taps on the button, the button will receive the tapmessage.

Here’s our interface.

@interface JVButton : NSObject {
  id _target;
  SEL _action;
}

- (void)initWithTarget:(id)target action:(SEL)action;

- (void)tap;

@end

And here’s the implementation.

@implementation JVButton

- (void)initWithTarget:(id)target action:(SEL)action
{
  self = [super init];
  if (self) {
    _target = target;
    _action = action;
  }
  return self;
}

- (void)tap
{
  [_target performSelector:_action withObject:self];
}

@end

The init method is simple, just some vanilla setup. We store the target and action for later. Notice that we don’t retain the target.

In the tap method, we ask the target to perform the action. We also pass the button as an argument to the action. There’s a bunch of variations on the basicperformSelector: method to help with things like passing arguments or delaying before performing: check Apple’s documentation for all of them.

The controller wired up to this button might look like this:

- (void)viewDidLoad
{
  button = [[JVButton alloc] initWithTarget:self action:@selector(customButtonTapped:)];
}

- (void)customButtonTapped:(id)sender
{
  NSLog(@"The custom button was tapped!");
}

If you look back at the example with UIBarButtonItem, you’ll see that they’re almost identical! Mimicking Apple’s code is easier than it sounds, right? :)

When to Use Target-Action

The best time to reach for this pattern is when:

  • You need or want one object to stay generic (e.g., the button class) but be able to call into custom code.
  • The generic object has exactly one action to perform (e.g., being tapped).

If the generic object has more than one action to perform, or needs to collect information from your custom code, your problem is probably better solved with the delegate pattern or the data source pattern.

【yasi】模拟一下,完整代码如下


// ======= MyButton.h ======

#import <Foundation/Foundation.h>

@interface MyButton : NSObject

@property id target;

@property SEL action;

-(id) initWithTarget:(id)target withAction:(SEL)action;

-(void) tap;

@end

// ======= MyButton.m ======

#import "MyButton.h"

@implementation MyButton

-(id) initWithTarget:(id)target withAction:(SEL)action {
    _target = target;
    _action = action;
    return self;
}

-(void) tap {
    [_target performSelector:_action withObject:self];
}

@end

// ======= MyViewController.h ======

#import <Cocoa/Cocoa.h>
#import "MyButton.h"

@interface MyViewController : NSObject

@property MyButton* button;

-(id) init;

-(void) customButtonTapped:(id)sender;

-(void) tapButton;

@end

// ======= MyViewController.m ======

#import "MyViewController.h"

@implementation MyViewController

-(id) init {
    if (self = [super init]) {
        _button = [[MyButton alloc] initWithTarget:self withAction:@selector(customButtonTapped:)];
    }
    return self;
}

-(void) customButtonTapped:(id)sender {
    NSLog(@"CUSTOM BUTTON TAPPED");
}

-(void) tapButton {
    if (_button) {
        [_button tap];
    }
}

@end

// ======= main.m ======

#import <Foundation/Foundation.h>
#import "MyViewController.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyViewController* view = [[MyViewController alloc] init];
        [view tapButton];
    }
    return 0;
}

运行结果:

> CUSTOM BUTTON TAPPED


相关文章推荐

深入探究 Objective-C Target Point

Tagged Pointer 介绍苹果对于Tagged Pointer特点的介绍: Tagged Pointer专门用来存储小的对象,例如NSNumber和NSDate Tagged Pointer指...

Objective-C Singleton Pattern

根据该文章写了一篇关于iOS单例模式的文章:http://duckrowing.com/2010/05/21/using-the-singleton-pattern-in-objective-c/ 单...

iPhone开发入门(6)--- Action与Objective-C

我们先来分析一下上一回初次接触的Objective-C代码。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...

Objective-C高级编程

  • 2016-07-23 11:11
  • 36.29MB
  • 下载

安全攻防——Objective-C代码混淆

iOS安全攻防

Objective-C 之 @property和@synthesize

很容易初学ios基础知识的巩固和理解,推荐,也谢谢文章作者辛苦的整理下来分享给大家
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)