[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和C++混编的要点

原帖地址  http://www.cocoachina.com/bbs/read.php?tid-9111-fpage-3.html Using C++ With Objective-C   ...
  • pizi0475
  • pizi0475
  • 2016年01月28日 12:02
  • 787

[深入浅出Cocoa]iOS网络编程之Socket

[深入浅出Cocoa]iOS网络编程之Socket 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 更多 Coc...
  • kesalin
  • kesalin
  • 2013年04月13日 20:51
  • 64950

配置Tomcat的访问日志格式化输出

转自:http://goon.iteye.com/blog/1814609
  • zhang_Red
  • zhang_Red
  • 2014年08月24日 20:07
  • 2125

Objective-C常用类的常用方法

第1篇 Objective-C准备篇   第1章 Objective-C学习环境准备    1.1 Objective-C基础    1.1.1 Objective-C的发展历程    1.1...
  • walden00
  • walden00
  • 2015年09月04日 15:44
  • 1326

Redis学习笔记--hiredis(C语言)

Redis, hiredis, C
  • feng973
  • feng973
  • 2017年03月13日 17:10
  • 172

String详解(三):正则表达式Pattern和Matcher详解

(一)利用String的split()对字符串进行切割 String text = "Hello, my name is liujianfeng"; System.out.println(Arrays...
  • jeffleo
  • jeffleo
  • 2016年08月12日 23:19
  • 4481

IOS深入学习(18)之Target-Action

1 前言     这节我们来学习一下Target-Action(目标-动作模式)。     英文原文: 2 详述     Target-Action是一种当一个时间发生时候,一个对象携带发送一个消息到...
  • u010013695
  • u010013695
  • 2013年10月11日 21:17
  • 3237

iOS开发笔记之十七——学习Cocoa和Cocoa Touch框架

1、 2、 3、参考资料
  • lizitao
  • lizitao
  • 2014年09月27日 22:41
  • 2907

初学cocoa开发:带你走入不一样的世界

最近由于项目需求,特意研究了一下mac端app的相关开发,一开始就想着在网上搜搜资料,后来才发现网上资料的太少了!也曾在cocoa China 那个osx 开发那个论坛上混了好久,但是效果一直不怎么好...
  • u012890071
  • u012890071
  • 2016年03月04日 16:46
  • 4029

[Cocoa]_[画直线、矩形、文字]

总结Cocoa下画直线、矩形、文字的方法,在界面开发种经常会遇到。 1.画直线,有两种方法。 (1)把直线看成是一个高度较小的的矩形 //(1)直线的本质是一个高度较小的矩形 NSRe...
  • yepeng2014
  • yepeng2014
  • 2015年10月13日 15:39
  • 1705
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[Objective-C] Cocoa's Target-Action Pattern
举报原因:
原因补充:

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