zoukankan      html  css  js  c++  java
  • objective-C中的"非正式协议"和“正式协议”

    objective-C中的接口与泛型

    先承认我是标题党,因为在obj-c的世界中,官方根本没有"接口"与"泛型"这样的说法。

    不过在obj-c中有二个与之接近的概念"非正式协议(interface)"与"正式协议(protocal)"。非正式协议在obj-c中的关键字虽然也是interface,但是这个跟c#中的接口(interface)并不完全相同。
    回忆一下前面学过的内容,我们定义一个类Sample时,总是会先生成一个Sample.h,代码如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #import <Foundation/Foundation.h>
     
    @interface Sample : NSObject {
     
    }
     
    -(void) HelloWorld;
     
    @end
    它表明Sample类中,约定了"应该"有一个名为HelloWorld的方法(注:我这里说的是应该,而不是必须),它只是一种君子协定

    如果我们在Sample.m中,并不遵守这个约定(即:不实现这个方法),编译时xcode会给出警告,如下图。但最后还是会编译成功(即:编译器对此是睁一只眼闭一只眼,默认了Sample类的这种不忠行为)

    上图中的提示:Incomplete implementation of class "Sample". 意为:Sample类并未完全实现interface中约定的方法。
    这就是obj-c中的协议跟c#中的接口不一样的地方:在c#中接口是强制必须实现的,否则编译这一关就过不了,而obj-c虽然在编译时会警告,但是最终能编译通过。


    正式协议(protocal)
    其实就是非正式协议(interface)换了一种写法而已,看上去更正规一些,语义上更强烈一些:要求采用该协议的类,"必须"实现协议中约定的方法。但是比较娱乐的是,即使是号称正式协议,编译器在编译时,遇到不守规矩的情况,仍然只是给出警告。(当然正式协议也有它存在的意义,后面会提到)
    这里我们定义一个IQuery的协议
    IQuery.h
    1
    2
    3
    4
    5
    @protocol IQuery
     
    -(void) Query:(NSString*) sql;
     
    @end
    除了把关键字@interface换成了@protocal,其它的基本上没变化。下面定义一个类DBQuery,并采用这个正式协议
    DBQuery.h
    1
    2
    3
    4
    5
    6
    7
    8
    #import <Foundation/Foundation.h>
    #import "IQuery.h"
     
    @interface DBQuery : NSObject<IQuery> {
     
    }
     
    @end
    注意这里的DBQuery:NSObject<IQuery>,它表明DBQuery继承自NSObject,同时要实现接口IQuery。
    DBQuery.m
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #import "DBQuery.h"
     
    @implementation DBQuery
     
    -(void) Query:(NSString *)sql
    {
        NSLog(@"Query is called. sql:%@",sql);
    }
     
    @end
    当然,如果在DBQuery.m中不实现方法Query,也能编译通过,只是会收到一个警告。
    也许到目前为止,你会觉得protocal跟interface比起来,都是类似的概念,protocal设计纯属多余。其实不然,protocal存在的一个重要意义在于:
    正式协议(protocal)可以将业务中的方法定义剥离出来,形成一个单独的文件,这跟传统OO中的提取接口是不谋而合的。如果遇到二个系统需要交换数据,可以制定一套双方都遵守的protocal,然后这二个系统中都把这个协议文件添加到项目中,实现它即可。这一功能,非正式协议(@interface)就做不到。(不信大家可以把NSObject<IQuery>中的IQuery改成其它类的interface定义名称试试,编译根本通不过)
    此外,obj-C 2.0中对正式协议还做了一些扩展,允许把正式协议中的方法标识为“必须实现(@requied)”和“可选实现(@optional)”二类,如果协议中的方法被标识为@optional,即使采用该协议的类不实现这些方法,编译器也不会给出警告。这赋予了正式协议更多的灵活性。示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @protocol IQuery
     
    @required
    -(void) Query:(NSString*) sql;
     
    @optional
    -(void) HelloWorld;
     
    @end

    有了@optional关键字以后,其实“非正式协议”在语义上完全可以被“正式协议”所取代,事实上Cocoa中的非正式协议都在逐渐被标有@optional方法的正式协议所代替。

    如果你在XCode的代码中,选中NSObject,右击-->Jump to Definition,会发现NSObject其实就是一个interface或protocal

    选择protocal NSObject 继续,会看到NSObject.h文件中关于protocal NSObject的定义

    同样的,你还可以看到interface NSObject的定义

    从这里可以看到,非正式协议的interface NSObject其实最终采用的还是正式协议protocal NSObject.

    也就是说,在obj-c的OO世界中,身为万物之祖的NSObject其实也就一个"正式协议”,所以从NSObject派生出的所有类,都只是在遵守一个或多个协议而已。


    另一个话题泛型

    在obj-c中,一切皆为指针。前面的学习中,我们已经接触到了一种特殊的类型id,它可以认为是一种特殊的指针:可以指向任何类型的对象。id 再加上正式协议,能够达到形似c#中泛型的效果(注:只是形似,并非神似)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #import <Foundation/Foundation.h>
    #import "IQuery.h"
     
    @interface DBQuery : NSObject<IQuery> {  
     
    }
     
    -(void) test:(id<IQuery>) obj;
    @end

    注意这里的 -(void) test:(id<IQuery>) obj; 这表明test方法接受一个任意类型的对象做为参数,但是该参数对象必须实现接口IQuery(也可以说成该参数对象必须采用正式协议IQuery),是不是跟c#中的

    void test(List<IQuery> obj) 长得很象? 

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    leetcode 122. Best Time to Buy and Sell Stock II
    leetcode 121. Best Time to Buy and Sell Stock
    python 集合(set)和字典(dictionary)的用法解析
    leetcode 53. Maximum Subarray
    leetcode 202. Happy Number
    leetcode 136.Single Number
    leetcode 703. Kth Largest Element in a Stream & c++ priority_queue & minHeap/maxHeap
    [leetcode]1379. Find a Corresponding Node of a Binary Tree in a Clone of That Tree
    正则表达式
    十种排序算法
  • 原文地址:https://www.cnblogs.com/songfeixiang/p/3733708.html
Copyright © 2011-2022 走看看