zoukankan      html  css  js  c++  java
  • ios消息的交互方式

    注意这些都是界面回传(即从第二个界面传到第一个界面,从第一个界面传到第二个界面的时候用第二个界面的属性即可)
     
    iOS消息的交互方式有4种,分别为:通知,代理,block,kvo
    现在我们对这个4中方式分别做详细的介绍
     
     
    通知
     
    //1通知的发送平台(信息的发布者)
     
       [[NSNotificationCenter defaultCenter] postNotificationName:@"enterTicket" object:nil userInfo:@{@"index":@"0"}];//userInfo:通知携带的内容详情@{key:index   value:需要跳转的位置}
     
     
    //2通知到的接收平台(信息的接收者)
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(enterTicket:) name:@"enterTicket" object:nil];
     
    //处理信息的方法
    - (void)enterTicket:(NSNotification *)noti {

        // 获取通知的详情信息
        NSString *indexStr = [noti.userInfo objectForKey:@"index"];
        NSInteger index = [indexStr integerValue];
       
     
    }
     
     
     
     
    //最后把通知移除
     [[NSNotificationCenter defaultCenter] removeObserver:self];
     
     
     
     
    代理(代理模式是ios中最常用的一种方式)
    代理就像打电话,我的代理我不拥有(weak),我可以适时(时候我定)告诉我的代理消息(实现代理方法,代理方法中有我的数据,代理方法中含有的参数就是我的数据),协议就像电话,要想通话必须有电话(签协议)然后确定接电话的人delegate = self,接到电话去干活(实现带来方法,如果我需要信息的话代理把信息返回给我,表示代理方法中有返回值)
     
     
    (李文全的理解)
    所谓代理就是代理者能够处理代理事件(对代理对对象进行处理,自己可以是自己的代理)(例如小孩需要照顾,可以设置一个代理(保姆)照顾,代理(保姆)实现代理的方法(照顾小孩),这个方法中传入一个小孩的属性,就可以对小孩的所有的属性进行处理)
     
     
    block 就只一个代码块,这个代码块当做参数传递,代码块的执行时间就是这个代码块的最终执行时间
     
     
    block 就像我的小弟,我拥有block的变量我实实在在的拥有它(Strong)(block变量的声明),我可以适时告诉他做揍人(block变量实现同是可以向他传递揍谁即参数)啥时候揍是我说了算(啥时候实现变量block,走完人给我反馈结果)
     
     谁主动(就是可以控制传值的时间,即控制什么时候调用block变量,和用代理调用代理方法)调用这个方法(代理方法或者block方法,谁里面就拥有block变量或者变量);消息的传递可以是相互的(即有参数有返回值)
    1 block的声明
     // 声明类型
     // sumBlockblock变量// 变量首字小写
        int(^sunBlock)(int,int);
     
        // SumB是类型别名//类名首字大写
        typedef int(^SumB) (int,int);
    2 block的定义
     
      sunBlock = ^(int x , int y ){
            return x +y;
        };// sunBlock是变量
     
     
          SumB bock_1 = ^(int x , int y ){
            return x +y;
        };// bock_1是变量
    3 block的调用
     
    sunBlock(1,2);//sunBlock变量的实现
     
    bock_1(1,2);//bock_1变量的实现
     
     
    方法的差数就是代码块
    调用方法就是传递参数(代码块)
    在这个方法中把这个代码块传递给新的block代码块
    实现这个新的block代码块
    要想实现两个对象数据的传递必须保证这两个对象同时存在(因为调用的对象必须有即信息传递方,blcok实现的对象必须有信息的接收方,用信息接收方的属性接收,故信息的接收方法是block代码的实现方,)
     
    注意 :block 只能从第二个视图传入第一个视图中,不能从第一个视图中传入第二个视图
             代理既可以从第一个视图传入第二个视图中,也能从第二个视图传入第一个视图
     
    block实现和调用在两个类中,
    实现 - 传差数(代码块)-调用-实现代码块内的内容
    当按完调用,后再走实现的代码(即最后的才走实现)
    走的是同一个视图控制器
     
     
     
    补充: 界面传值和对象传值是不一样的(明天)
     
     
     
     
     
     
     
     

    二、Protocol(协议)

    (一)简介

    1.Protocol:就一个用途,用来声明一大堆的方法(不能声明成员变量),不能写实现。

    2.只要某个类遵守了这个协议,就拥有了这个协议中的所有方法声明。

    3.只要父类遵守了某个协议,那么子类也遵守。

    4.Protocol声明的方法可以让任何类去实现,protocol就是协议。

    5.OC不能继承多个类(单继承)但是能够遵守多个协议。继承(:),遵守协议(< >)

    6.基协议:<NSObject>是基协议,是最根本最基本的协议,其中声明了很多最基本的方法。

    7.协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明。

    (二)基本使用

     
     
    创建一个协议
     
    遵守协议
     
     
     
    完成协议中声明的方法的实现
     
     
     
     
    测试程序
     
     
     

    1.协议的定义

    @protocol 协议名称 <NSObject>

    //方法声明列表

    @end;

    2.如何遵守协议

    (1)类遵守协议

    @protocol 类名:父类名 <协议名称1,协议名称2>

    @end

    (2)协议遵守协议

    @protocol 协议名称 <其他协议名称>

    @end;

    3.协议方法声明中的关键字

    (1)required (默认)要求实现,若没有实现则警告但不报错

    (2)Optional 不要求实现

    4.定义变量时遵守协议的限制

    类名<协议名称> *变量名    NSObject<.Myprotocol> *obj;

    Id  <协议名称>  变量名   id  <.Myprotocol> obj1;

    5.Property中声明的属性也可以做遵守协议的限制

    @property (nonatomic ,strong ) 类名<协议名称> *属性名;

    @property (nonatomic ,strong ) id<协议名称>  属性名;

    6.补充知识:协议本身写在.h头文件中,但也可以定义在任何地方。当这个协议只有这个类使用遵守时,一般把协议写在这个类里边,当这个协议需要多个类去实现时,就写在外边单独的文件中。

     
     
     
     
     
     
     
     
     
     
     
     
    代理就是委托对象
     
     
    objective -c 协议(protocol)
    协议(protocol)是object - c 中一个非常重要的语言特性,从概念上讲,非常类似于java中接口,一个协议其实就是一系列中有关联的方法的集合(为方便后面叙述,我们把这个协议命名为myProtocol)协议中的方法并不是由协议本身去实现,相反而是由遵循这个协议的其他类来实现,换句话说 协议myprotocol只是完成对协议函方法的声明而并并不管这些新协议方法的具体实现
     
    声明一个协议的语法非常简单
    @protocol myProtocol <NSObject>
    @requiered
    -(void)protocolNameA:(NSString*)string;
    @optional
    -(void)protocolNameB:(NSString*)string;
    @end
    第一行是声明这个协议的名字为myProtocol.尖括号中的NSObject本身也是一个协议,其中定义了很多基本的协方法。比如performSelector,isKindOfClass ,respond to Selector conformsToProtocol ,retain release 等
    协议接口分为 required 和 optional 两类 required 顾名思义是说遵守这个协议的那个类“必须要”实现的接口,而optional则是可以实现也可以不实现。协议接口的定义和普通的方法定义是一样的
    最后一行@end 表示协议定义结束。这个协议的定义通常是在.h文件中.。
     
    定义一个类遵循这个协议
    @interface myClass <myProtocol>
    @intreface myClass :NSObject<myProtocol>
    @intreface myClass : NSObject<myProtocol,NScoding>
    上面分别是三种不同的情况,编译的时候编译器会自动检查myClass是否实现了myProtocol中的必要的(@requierd)接口。如果没有实现则会发送一个警告信息,另外需要注意的是,如果有有继承myClass的子类,这些子类也是会自动遵循myClass所遵循的协议的,而且也可以重载这些接口
     
     
    为什么需要协议?
    苹果的官方文档指出三个原因
    To declare mothods that others are expected to implement
    To declare the interface to an object while concrealing its class
    To capture similarties among classes that are not hierarchicallu related
    其实还有第四个很重要的原因,那就是减少继承类的复杂性。一个经典的例子就是 IOS UI框架里面的UITableViewController类。假如没有“协议”功能。用户就必须选择用继承和重载接口的方法来实现复杂的UI控制以及其他事件的处理——这就对基类的设计提出了更大的挑战了。对于像这样一个table view,一个很好的实现方法就是采用协议,由协议里的接口来控制不同的数据源以及各种复杂的用户操作。UIKit中设计了两个很好的协议UITableViewDelegate,UITableViewDataSource来实现UITableViewController的控制。任何遵循这两个协议的类都可以实现对UITableView的控制。

    关于 id类型的运用:(不喜欢钻牛角尖的朋友,可以略过这一部分)

     
    id 类型在iOS中是一个通用类型,有点类似C语言的void*类型。编译器不能检查到定义为id类型的变量的实际类型,id类型的识别是发生在运行时阶段。但是我们可以用 id<protocol_name> obj;这样的语法形式在编译阶段就可以让编译器知道obj只可以发送protocol_name中的消息,如果所发送的消息不在protocol_name中,编译器会给一个警告信息“Instance method 'xxxx:' not found......”。这种情况多用于代理模式的实现,比如某一个类有一个delegate 的property:
     
    id <myProtocol>delegate;
    这样,在编译阶段我们就可以知道用delegate所发送的消息是不是在它所遵循的myProtocol中的消息。好了, 到这里笔者钻起了牛角尖,我把id后面的 <myProtocol>删掉,然后用delegate发送一个并不存在于myProtocol中的消息,结果编译器还是给了“Instance method 'xxxx:' not found......”的警告信息。更奇怪的是,当发送一个存在于myProtocol中的消息时,编译器竟然没有这样的警告信息。这两个测试并不能说明之前的解释是错误的,姑且认为id<myProtocol> delegate这种写法是为了便于知道这个delegate遵循了myProtocol的协议吧.
     
     
     
     
     
     
    委托(delegate)也叫代理是iOS开发中常用的设计模式。我们借助于protocol(参考博文:objective-c协议(protocol))可以很方便的实现这种设计模式。
     

    什么是代理?

    苹果的官方文档给了很清晰的解释:

    Delegation is a simple and powerful pattern in which one object in a program acts on behalf of, or in coordination with, another object. The delegating object keeps a reference to the other object—the delegate—and at the appropriate time sends a message to it. The message informs the delegate of an event that the delegating object is about to handle or has just handled. The delegate may respond to the message by updating the appearance or state of itself or other objects in the application, and in some cases it can return a value that affects how an impending event is handled. The main value of delegation is that it allows you to easily customize the behavior of several objects in one central object.

    意译一下就是:代理是一种简单而功能强大的设计模式,这种模式用于一个对象“代表”另外一个对象和程序中其他的对象进行交互。 主对象(这里指的是delegating object)中维护一个代理(delegate)的引用并且在合适的时候向这个代理发送消息。这个消息通知“代理”主对象即将处理或是已经处理完了某一个事件。这个代理可以通过更新自己或是其它对象的UI界面或是其它状态来响应主对象所发送过来的这个事件的消息。或是在某些情况下能返回一个值来影响其它即将发生的事件该如何来处理。代理的主要价值是它可以让你容易的定制各种对象的行为。注意这里的代理是个名词,它本身是一个对象,这个对象是专门代表被代理对象来和程序中其他对象打交道的。

     

    Cocoa中的代理

    Cocoa Touch框架里大量使用了代理这种设计模式,在每个UI控件类里面都声明了一个类型为id的delegate或是dataSource,查看Cocoa的头文件可以发现很多如下的属性:

    @property(nonatomic, assign)id<UIActionSheetDelegate> delegate;   // weak reference

    通常格式为@property(nonatomic, assign)id<protocol_name> delegate;  即这个代理要遵循某一个协议,也就是说只有遵循了这个协议的类对象才具备代理资格。这同时也要求了代理类必须在头文件中声明遵循这个protocol_name协议并实现其中的@required方法,@optional的方法是可选的。

     
    以UIActionSheet为例,我们定义一个View,当点击这个View中的某一个按钮时触发UIActionSheet, 当用户对UIActionSheet完成了某一项操作,比如Destruct按钮被按下,或是cancel按钮被按下,UIActionSheet会发送消息给delegate,由delegate完成对用户操作的响应,比如打印一个字符串到屏幕上。图示说明如下:
     
     
    首先,我们创建一个基于tab的工程,在FirstViewController.h中添加代码,使这个类遵循UIActionSheetDelegate协议:
    1. @interface FirstViewController : UIViewController <UIActionSheetDelegate> 
     
    在View中添加一个按钮用于触发这个ActionSheet,然后编写这个按钮的响应代码: 
    - (IBAction)invokeActionSheet:(id)sender {
     
        UIActionSheet *actionSheet = [[UIActionSheet alloc]
                                      initWithTitle:@"Delegate Example"
                                      delegate:self // telling this class(ViewController) to implement UIActionSheetDelegate
                                      cancelButtonTitle:@"Cancel"
                                      destructiveButtonTitle:@"Destruct"
                                      otherButtonTitles:@"Button 1",@"Button 2",nil];
     
        [actionSheet showInView:self.tabBarController.view];
        [actionSheet release];
    }
     
     
    注意,上面有一个很重要的设置就是参数中有个delegate:self,这个设置就是指明了UIActionSheet的代理为self, 也即FirstViewController
    然后在FirstViewController.m中实现UIActionSheetDelegate中的方法:
     
    #pragma mark --UIActionSheet delegate methods
    - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
        switch (buttonIndex) {
            case 0:
                self.myTextFromActionSheet.text = @"Action Destructed!";
                break;
            case 1:
                self.myTextFromActionSheet.text = @"Action Button 1 Clicked!";
                break;
            case 2:
                self.myTextFromActionSheet.text = @"Action Button 2 Clicked!";
                break;
            case 3:
                self.myTextFromActionSheet.text = @"Cancel Button Clicked!";
                break;
            default:
                break;
        }
     
    }
     

    上面的几步我们完成了对Cocoa中UIActionSheet已有代理的运用。然而我们很多时候需要自己编写定制的代理,该如何实现呢?

    自定义代理

    我们要做的是,创建一个view,自定义一个代理实现更新这个view中的字符串。上面我们已经创建好了一个tab工程,借用里面的second view。我们拖一个按钮到上面命名为ChangeText,响应函数为- (IBAction)changeText:(id)sender;点击这个按钮进入一个modal view 名为ChangeTextView,我们在ChangeTextView中输入一个字符串并在退出这个view后把这个字符串更新到second view上面。如何实现modal view和second view之间的数据传递呢?那就是代理!谁的代理?ChangeTextView的代理!因为我们直接在ChangeTextView中输入数据,需要由代理把输入的字符串反馈到second view上面去。

    1、创建一个新的类ChangeTextViewController,并创建相应的xib文件。

     
    2、在ChangeTextViewController.h中声明一个协议ChangeTextViewDelegate:
    @protocol ChangeTextViewDelegate <NSObject>
     
    - (void) textEntered:(NSString*) text;
     
    @end
    和UIActionSheet类似,在ChangeTextViewController中我们也需要添加一个代理的声明:
    @property (assign, nonatomic) id<ChangeTextViewDelegate> delegate;
    3、我们还需要在ChangeTextViewController.xib中添加一个按钮save,当按下这个按钮会返回到second view中,并更新字符串。对save按钮的响应函数为:
    - (IBAction)saveButtonClicked:(id)sender {
        //Is anyone listening
        if([delegate respondsToSelector:@selector(textEntered:)])
        {
            //send the delegate function with the amount entered by the user
            [delegate textEntered:textEntered.text];
        }
     
        [self dismissModalViewControllerAnimated:YES];
    }
    [delegate textEntered:textEntered.text];这句代码的含义就是ChangeTextViewController通知代理,textEntered这个事件发生了,对textEntered这个消息的实现,即如何响应这个textEntered的事件由代理来实现。在本例中,SecondViewController就是ChangeTextViewController对象的代理。所以,我们要对SecondViewController做相应的设置使其满足代理的条件。首先,在SecondViewController.h中声明遵循协议ChangeTextViewDelegate。然后编辑ChangeText按钮的响应函数- (IBAction)changeText:(id)sender;
    - (IBAction)changeText:(id)sender {
        ChangeTextViewController *CTViewController = [[ChangeTextViewController alloc] initWithNibName:@"ChangeTextViewController" bundle:nil];
        //Assign this class to the delegate of ChangeTextViewController,
        //remember to make thie ViewController confirm to protocol "ChangeTextViewDelegate"
        //which is delared in file ChangeTextViewController.h
        CTViewController.delegate = self;
        [self presentModalViewController:CTViewController animated:YES];
    }
    注意,CTViewController.delegate = self;这句实现了SecondViewController成为ChangeTextViewController对象的代理
     
     
     
     
    block (代码块)
     
     
     
    //信息的发布者
     
    信息发布者的类
     
    1 Blcok 类型重命名(这个Block用Person代替,这个Block中传入参数是个字符串,返回的参数是void)
    typedef void(^PersonBlock)(NSString *name);
     
    2在这个类中声明这种Block类型的属性
    PersonBlock _block;
     
    3这个类中声明一个带有Block类型参数的方法
    - (void)getPersonName:(PersonBlock)block;
     
     
    4上面方法的实现
    - (void)getPersonName:(PersonBlock)block {

        _block = block;
    }
     
    //信息的接受者
     
    初始化信息发布者的类
     Person *per = [[Person alloc] init];
    调用信息发布者的类中带有Block参数的方法
    [per getPersonName:^(NSString *name) {
          
            NSLog(@"%@",name);
           
        }];
     
    Block是什么?苹果推荐的类型,效率高,在运行中保存代码。用来封装和保存代码,有点像函数Block可以在任何时候执行。
    Block和函数的相似性:(1)可以保存代码(2)有返回值(3)有参数(4)调用方式一样
     
        
     
     (二)基本的使用
    (1)定义BLOCK变量
    Int(^SumBlock)(int,int) ;//有参数,返回值类型为int
    Void (^MyBlock)()//无参数,返回值类型为空
    (2)利用block封装代码

    (3)Block访问外部变量

    1)Block内部可以访问外部变量;

    2)默认情况下,Block内部不能修改外部的局部变量

    3)给局部变量加上__block关键字,则这个局部变量可以在block内部进行修改。

    (4)利用typedef定义block类型(和指向函数的指针很像)

    Typedef int(^MyBlock)(int ,int);

    以后就可以利用这种类型来定义block变量了。

    MyBlock a,b;  

    a=^(int a,int b){return a-b;};

    MyBlock b2=^(int n1,int n2){return n1*n2;}
     
     
    3..
     
     
     
     
     
    //注意事项
     
    根据程序加载顺序
    初始化
     
     
     
    //——————————————————信息发布者的类.h文件-------------------------------------
    #import <Foundation/Foundation.h>
     
    typedef void(^PersonBlock)(NSString *name);
    @interface Person : NSObject
    {

        PersonBlock _block;

    }



    - (void)getPersonName:(PersonBlock)block;



    @end
     
     
    //--------------------信息发布者的类.m文件——————————————————//
     
    #import "Person.h"

    @implementation Person
    {

        NSString *_name;

    }
    - (id)init {

        if (self = [super init]) {
           
           
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                _name = @"晓彬";
               
                _block (_name);
               
            });
           
        }

        return self;
    }



    - (void)getPersonName:(PersonBlock)block {

        _block = block;

    }


    @end
     
    //信息接受者文件
    #import <UIKit/UIKit.h>

    @interface ViewController : UIViewController

    @end

    #import "ViewController.h"
    #import "Person.h"

    @interface ViewController ()

    @end

    @implementation ViewController

    - (void)viewDidLoad {
        [super viewDidLoad];
       
       
        Person *per = [[Person alloc] init];
       
        [per getPersonName:^(NSString *name) {
          
            NSLog(@"%@",name);
           
        }];
       
       
       
       
       
    }
    @end
     
     
     
     
    kvo
      //直接修改属性值,不能触发KVO
        //    _happyValue--;
       
        //1.调用set方法来修改属性值,可以触发KVO
        //    self.happyValue = _happyValue - 1;
       
        //2.使用KVC修改属性值,也能触发KVO
        NSInteger value = _happyValue;
        NSNumber *valueNumber = [NSNumber numberWithInteger:--value];
        [self setValue:valueNumber forKey:@"happyValue"];
       
        //修改hungryValue
        self.hugryValue = _hugryValue - 2;
     
     
     
    _children = child;
           
            //使用KVO来监听小孩属性值的变化
            //_children 作为被观察者
            //observer 事件的观察者:self
            //KeyPath:监听被观察者的某个属性值
            //options:
            //NSKeyValueObservingOptionNew:监听观察修改后的属性值
            //NSKeyValueObservingOptionOld:监听观察修改前的属性值
            [_children addObserver:self
                        forKeyPath:@"happyValue"
                           options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
                           context:nil];
           
       
    /
     
     block 是匿名的代码端
     
    // 声明和实现
    (^block名  )(参数){
     代码段
    }
     
     
     
     
    // 调用
    block 变量
    /
     
     
  • 相关阅读:
    人生苦短_我用Python_javascript_var_function_简单笔记_001
    人生苦短_我用Python_logging日志操作_011
    人生苦短_我用Python_configparser/yaml对配置文件读取/写入操作_010
    人生苦短_我用Python_pymysql库对Mysql数据库操作_009
    人生苦短_我用Python_openpyxl库读取Excel文件数据_008
    人生苦短_我用Python_Try_Exception异常捕捉_007
    命令行下编译打包安卓apk
    构建微服务实验环境
    Docker的安全问题以及一些预防方案
    Go 微服务实践
  • 原文地址:https://www.cnblogs.com/apple-image/p/5345564.html
Copyright © 2011-2022 走看看