• Objective-C:模拟按钮点击事件理解代理模式


      OC中的协议(Protocol)和和.NET中的接口(Interface)类似,简单来讲,就是一系列方法的列表,其中声明的方法可以被任何类实现。不同的是,在.NET中,如果某个类实现了一个接口,就必须实现这个接口中声明的所有方法;但在OC中,可以不实现协议中声明的所有方法,需要用到某些功能,就去实现对应的方法即可。

    这种模式一般称为代理模式。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View(UI控件)和Controller(控制器)的解耦

      监听思想:如果想让某个对象能够监听某个控件的动作,就可以让这个对象实现特定的协议,好抽象~。说人话,比如,如果想要用户点击某个按钮后,APP能够做一些事情(例如返回到上一个界面),需要给按钮设置监听器监听按钮的点击事件,然后“返回上一个界面”这个事情就交给监听器去做。因为需要作出特定的响应,所以不是所有的对象都可以成为按钮的监听器的。那什么样的对象才能充当按钮的监听器呢?条件是:实现某个协议里的方法(这个协议一般在需要被监听的对象内部声明)。

      以监听按钮的点击事件为例:

    Button:我可以被点,谁来监听我啊?

    //Button.h头文件
    //和分类Catelogy一样,协议也可以写在其他文件中,但一般写在.h文件中
    #import
    <Foundation/Foundation.h> @class Button; // <>代表实现某个协议 @protocol ButtonDelegate <NSObject>//协议也可以实现其他协议,定义的该协议实现了最根本的协议NSObject - (void)onClick:(Button *)btn;//这个方法是留给需要实现该协议的对象去调用 @end @interface Button : NSObject @property (nonatomic, retain) id<ButtonDelegate> delegate;//按钮应提供一个属性来设置监听器,delegate就是按钮的监听器,不然按钮和其监听器无法通信

    //不需要线程安全,故nonatomic;它是个对象,最好用retain;不知道声明类型的对象会成为自己的监听器,故id
    - (void)click;//这个方法是按钮自己调用的,用来模拟点击按钮事件,调用该方法相当于点击了按钮

    @end
    //Button.m实现文件
    #import
    "Button.h" @implementation Button - (void)dealloc { [_delegate release]; [super dealloc]; } - (void)click { // 如果_delegate实现了onClick:这个方法 if ( [_delegate respondsToSelector:@selector(onClick:)] ) { // 按钮被点击了,就应该通知监听器.并且告诉监听器哪个按钮被点击了 [_delegate onClick:self]; } else { NSLog(@"监听器并没有实现onClick:方法"); } } @end

    ButtonListerner:我想监听你呢!

    //ButtonListerner.h头文件
    #import
    <Foundation/Foundation.h> // 对协议进行提前声明,跟@class的用途是一致的 @protocol ButtonDelegate; @interface ButtonListener : NSObject <ButtonDelegate>//要做Button的监听器,就要实现我Button定的协议 @end
    //ButtonListerner.m实现文件
    #import
    "ButtonListener.h" #import "Button.h" @implementation ButtonListener - (void)onClick {//实现Button协议里为我准备的onClick方法 NSLog(@"ButtonListener已经监听到按钮被点击了"); } @end

    main:我来检查下,看ButtonListerner可不可以监听Button了。

    //main函数中实现对按钮的监听
    #import
    <Foundation/Foundation.h> #import "Button.h" #import "ButtonListener.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 初始化一个按钮 Button *button = [[[Button alloc] init] autorelease]; // 初始化一个按钮的监听器 ButtonListener *listener = [[[ButtonListener alloc] init] autorelease];// 设置按钮的监听器 button.delegate = listener; NSLog(@"button:%@", button); // 点击按钮 [button click]; } return 0; }

      main函数运行后打印的结果是:ButtonListener已经监听到按钮被点击了。

      这表明ButtonListerner里的onClick方法被调用了,奇怪,在main函数中我们并没有调用这个方法呀。这就是我们想要的结果:当“[button click];”执行(前面说过,这表示Button被点击了),然后某些事情就自动处理了(比如这里ButtonListerner里的onClick方法)。再啰嗦几句吧。

      理解这个监听过程,主要注意这两个方法:

    • - (void)click方法:这个方法是按钮自己实现的,相当于事件的发生;
    • - (void)onClick:方法:这个方法是代理去实现的。

      上面的例子显示了当按钮Button调用自己的click方法时,跟着按钮的代理ButtonListener的onClick方法也被调用了,这样就达到了监听按钮点击事件的目的。也就是说,当我们想让一个按钮被点击后,其他地方能够进行一些各种各样的操作时,我们不需要在按钮内部去实现,在它的代理对象内部去实现就可以啦,这不就达到了解耦的目的嘛。

      协议也可以写在一个新建的协议文件里,并且实现该协议的对象可以对该协议里的方法选择性地去实现。哪些方法是必须实现的,哪些方法又是可以选择性实现的呢?这取决于该方法是如何被声明的。

    定义两个协议:

    //StudyDelegate.h 协议文件
    #import <Foundation/Foundation.h>
    
    @protocol StudyDelegate <NSObject>
    // 默认就是@required
    - (void)test3;
    
    // @required表示必须实现的方法
    // 虽然字面上说是必须实现,但是编译器并不强求某个类进行实现
    @required
    - (void)test;
    
    - (void)test1;
    
    // @optional表示可选(可实现也可不实现)
    @optional
    - (void)test2;
    @end
    //LearnDelegate.h协议文件
    #import <Foundation/Foundation.h>
    
    @protocol LearnDelegate <NSObject>
    
    @end

     定义一个学生类,该类的对象用来实现上面的协议:

    //Student.h
    #import
    <Foundation/Foundation.h> @protocol StudyDelegate, LearnDelegate; @interface Student : NSObject <Study, Learn> @end
    //Student.m
    #import
    "Student.h" #import "StudyDelegate.h" #import "LearnDelegate.h" @implementation Student @end

    判断Student类型的对象是否实现了协议:

    #import <Foundation/Foundation.h>
    #import "Student.h"
    
    @protocol StudyDelegate;
    
    int main(int argc, const char * argv[])
    {
    
        @autoreleasepool {
            Student *stu = [[[Student alloc] init] autorelease];
            
            // 注意:OC是弱语法的,对类型要求不严格
            // NSString *stu = [[[Student alloc] init] autorelease];
            // [stu stringByAbbreviatingWithTildeInPath];
            
            // conformsToProtocol:判断是否遵守了某个协议
            if ([stu conformsToProtocol:@protocol(StudyDelegate)]) {
                NSLog(@"Student遵守了StudyDelegate这个协议");
            }
            
            // respondsToSelector:判断是否实现了某个方法
            if ( ![stu respondsToSelector:@selector(test)] ) {
                NSLog(@"Student没有实现test这个方法");
            }
        }
        return 0;
    }

    运行结果是:Student遵守了StudyDelegate这个协议;Student没有实现test这个方法;验证了刚才的说法:可以遵守协议,但不实现该协议里声明的方法。遵守协议即实现协议。

    注意,selector就相当于方法(消息),发送消息就是调用方法

  • 相关阅读:
    兼容Android 和 ios JavaScript copy paste
    hello-循环神经网络(RNN)原理
    236. Lowest Common Ancestor of a Binary Tree
    Tensorflow 之模型内容可视化
    CUDA,cudnn一些常见版本问题
    MongoDB国内学术研究(部分)
    Unsupported major.minor version 52.0
    WebService的两种方式SOAP和REST比较
    廖雪峰老师的git在线教程
    MAVEN ERROR : Dynamic Web Module 3.0 requires Java 1.6 or newer
  • 原文地址:https://www.cnblogs.com/yif1991/p/5065349.html
Copyright © 2020-2023  润新知