非正式协议/正式协议

1、非正式协议(interface)
 
非正式协议在Obj-c中关键字虽然是interface,但是这个和C#中的接口并不完全相同。
 
回忆之前学过的内容,我们定义一个类Sample时,会生成一个Sample.h,代码如下:
 
#import <foundation foundation.h>
 
@interface Sample:NSObject {
 
}
 
-(void) HelloWorld;
 
@end
 
它表明Sample类中,约定了应该有一个名为HelloWorld的方法(注:这里说的是应该,而不是必须)
 
它只是一种君子协议。
 
如果我们在Sample.m中,并不遵守这个约定(即不实现这个方法),编译器会给出警告,但是还时会编译成功
 
编译警告:Incomplete implementation of class "Sample". 意为:Sample类并未完全实现interface中约定的方法。

这就是obj-c中的协议跟c#中的接口不一样的地方:在c#中接口是强制必须实现的,否则编译这一关就过不了,而obj-c虽然在编译时会警告,但是最终能编译通过。
 
2、正式协议(protocal)

其实就是非正式协议(interface)换了一种写法而已,看上去更正规一些,语义上更强烈一些:要求采用该协议的类,"必须"实现协议中约定的方法。

但是比较娱乐的是,即使是号称正式协议,编译器在编译时,遇到不守规矩的情况,仍然只是给出警告。(当然正式协议也有它存在的意义,后面会提到)

这里我们定义一个IQuery的协议
 
IQuery.h

@protocol IQuery
 
-(void)Query:(NSString*)sql;
 
@end
 
除了把关键字@interface换成了@protocal,其它的基本上没变化。下面定义一个类DBQuery,并采用这个正式协议

DBQuery.h
 
#import <Foundation/Foundation.h>
 
#import "IQuery.h"
 
@interface DBQuery : NSObject<IQuery> {
 
}
 
@end
 
注意这里的DBQuery:NSObject<IQuery>,它表明DBQuery继承自NSObject,同时要实现接口IQuery。

DBQuery.m

#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,即使采用该协议的类不实现这些方法,编译器也不会给出警告。这赋予了正式协议更多的灵活性。示例如下:
 
@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派生出的所有类,都只是在遵守一个或多个协议而已。
 
3、另一个话题"泛型"
 
在obj-c中,一切皆为指针。前面的学习中,我们已经接触到了一种特殊的类型id,它可以认为是一种特殊的指针:可以指向任何类型的对象。

id 再加上正式协议,能够达到形似c#中泛型的效果(注:只是形似,并非神似)
 
#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) 长得很像?
 
4、实例演示

自己的理解:
 
其实协议就相当于Java中的interface或者C++中的virtual class
 
举个例子说说:
 
首先我们声明一个协议

@protocol MyProtocol

  - (void)myProtocolMethod;

@end

@interface TestA
{
   //说明类B应该支持遵守myProtocol这个协议
   TestB<myProtocol>* pB; 
}

@interface TestB: NSObject<myProtocol>{
 
}
 
现在我们看如果我们在TestA里面做完了一件事情,然后要通知TestB。那就要通过myProtocol来实现,那通过pB我们就可以给myProtocol发通知,那TestB也就收到消息了
 
下面再附上一个用协议实现回调函数的例子
 
自己编写回调函数,实现动态加载数据,加载完数据之后就利用回调函数通知给前台页面,显示相应数据的界面。

在iphone中利用协议可以很容易的实现回调函数,后台加载数据,然后显示在前台页面。
 
实现一个显示文字为测试的视图,然后经过3秒钟测试文字变为回调函数文字,实现的代码如下:
 
#import <UIKit/UIKit.h>
@protocol NoteDelegate //声明协议

 //回调函数
 -(void)messageCallBack:(NSString *)string;

@end

调用协议:
 
#import <Foundation/Foundation.h>
#import "NoteDelegate.h"
@interface ManagerMessage : NSObject {
   id<NoteDelegate> *noteDelegate;
}
@property (nonatomic,retain) id<NoteDelegate> *noteDelegate;
-(void)startThread;
@end
 
#import "ManagerMessage.h"
@implementation ManagerMessage

@synthesize noteDelegate;
 
  //开始一个线程
  -(void)startThread
  {
     //启动线程
     [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(targetMethod:) userInfo:nil repeats:NO];
  }
 
  -(void)targetMethod:(NSString *)string
  {
     if (self.noteDelegate!=nil)
     {
        //完成线程 调用回调函数
        [self.noteDelegate messageCallBack:@"回调函数"];//在这通过协议进行回调
     }
  }

@end
 
前台页面实现:
 
#import "IphoneDeleteViewController.h"

#import "ManagerMessage.h"

@implementation IphoneDeleteViewController

@synthesize textView;

//回调函数
-(void)messageCallBack:(NSString *)string
{
   self.textView.text=string;//前台收到回调信息 给了textView
}

- (void)viewDidLoad {
   [super viewDidLoad];
   self.textView.text=@"测试";
   ManagerMessage *message=[[ManagerMessage alloc] init];
   //通知调用协议
   message.noteDelegate=self;
   [message startThread];
   [message release];
}

-(void)didReceiveMemoryWarning
{
   [super didReceiveMemoryWarning];
}
 
-(void)viewDidUnload
{
   self.textView=nil;
}

-(void)dealloc
{
   [self.textView release];
   [super dealloc];
}

@end

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值