转自:http://www.cnblogs.com/chijianqiang/archive/2012/06/22/objc-category-protocol.html

面向对象永远是个可以吐槽的话题,从开始提出到推崇备至,到充满质疑,一路走来让人唏嘘不已。面向对象的思想可谓历史悠久,20世纪70年代的Smalltalk可以说是面向对象语言的经典,直到今天我们依然将这门语言视为面向对象语言的基础。

面向对象是大部分编程语言的基本特性,像C++、Java、Objective-C这样的静态语言,Ruby、Python这样的动态语言都是面向对象的语言。但是如何编写面向对象的程序却一直是困扰人们的话题,即使是Smalltalk,也有人认为这是一个有缺陷的面向对象的语言实现。
 
我在2010年翻译过的一篇InfoQ的文章,《面向对象编程──走错了路》中提到,面向对象编程的三个原则是:基于消息传递机制,对象分离和多态。文章中还举了Erlang例子,认为Erlang具备了这些原则,“所以可能是唯一的面向对象语言”。除了之前提到的三个特征,单继承和动态类型也被引用为面向对象语言的“绝对需求”。基于这些考虑,文章指出,Smalltalk在实现对象思想时的“错误”──例如,只关注状态和行为,在类和基于映像的语言里缺乏良好的并发模型和消息机制。
 
这篇文章中的核心就是,面向对象思想中除了对象的状态、行为,还应该关注其并发机制、消息机制,后者更为重要。这一点事实上是我在接触了Objective-C之后才有了更深入的体会。
 
Ojbective-C的语法设计主要基于Smalltalk,除了提供传统的面向对象编程特性之外,还增加了很多类似动态语言Ruby、Python才具有的特性,例如动态类型、动态加载、动态绑定等等,同时强化了消息传递机制和表意(Intention Revealing Interface)接口的概念。
 
—消息—
消息传递模型(Message Passing)是Objective-C语言的核心机制。在Objective-C中,没有方法调用这种说法,只有消息传递。在C++或Java中调用某个类的方法,在Objective-C中是给该类发送一个消息。在C++或Java里,类与类的行为方法之间的关系非常紧密,一个方法必定属于一个类,且于编译时就已经绑定在一起,所以你不可能调用一个类里没有的方法。而在Objective-C中就比较简单了,类和消息之间是松耦合的,方法调用只是向某个类发送一个消息,该类可以在运行时再确定怎么处理接受到的消息。也就是说,一个类不保证一定会响应接收到的消息,如果收到了一个无法处理的消息,那么程序就是简单报一个错。甚至你可以向一个值为nil的空对象发送消息,系统都不会出错或宕掉。这种设计本身也比较符合软件的隐喻。
 
在表意接口(Intention Revealing Interface)方面,Objective-C也是设计的比较出色的语言。面向对象语言的特性之一就是通过API把实现封装起来,为上层建筑提供服务。但是需要注意的一点就是,你封装的API最好能够让调用者看到接口描述就知道怎么使用。如果为了使用一个API必须要去研究它的实现,那么就失去了封装的意义。Objective-C通过显式的API描述,让开发者不自觉的写出满足表意接口的API,比如下图中的API描述。

 
上图中描述了一个传统意义的实例方法,但和Java或C++不同的是,其方法关键字由多个字符串组成,在这个例子是insertObject和atIndex,(id)anObject和(NSUInterger)index分别表示参数类型和参数名称。整个方法看上去就像一个英语句子,我们可以很容易的知道,这个方法就是在索引为index处插入一个对象。如果你是从其他语言转到Objective-C,那么开始的时候会感觉这种写法有些繁复,但是一旦理解并习惯了你会感受到其巨大的好处,这种写法会强制你写出优美易读的代码和API,而且有了XCode强大的提示功能,再长的方法也是一蹴而就。
 
下面我们来说说多态和继承。
 与Java一样,Objective-C一样不支持多重继承,但是通过类别(Category)和协议(Protocol)可以很好的实现代码复用和扩展。
 
—Category—
首先我们来谈谈Category。
 
Objective-C提供了一种与众不同的方式——Category,可以动态的为已经存在的类添加新的行为。这样可以保证类的原始设计规模较小,功能增加时再逐步扩展。使用Category对类进行扩展时,不需要访问其源代码,也不需要创建子类。Category使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中。
 
实现起来很简单,我们举例说明。
  1. SomeClass.h
  2. @interface SomeClass : NSObject{
  3. }
  4. -(void) print;
  5. @end 
 
这是类SomeClass的声明文件,其中包含一个实例方法print。如果我们想在不修改原始类、不增加子类的情况下,为该类增加一个hello的方法,只需要简单的定义两个文件SomeClass+Hello.h和SomeClass+Hello.m,在声明文件和实现文件中用“()”把Category的名称括起来即可。声明文件代码如下:
 
  1. #import "SomeClass.h"
  2.  
  3. @interface SomeClass (Hello)
  4. -(void)hello;
  5. @end
实现文件代码如下
  1. #import "SomeClass+Hello.h"
  2. @implementationSomeClass (Hello)
  3. -(void)hello{
  4. NSLog (@"name:%@ ", @"Jacky");
  5. }
  6. @end 
其中Hello是Category的名称,如果你用XCode创建Category,那么需要填写的内容包括名称和要扩展的类的名称。这里还有一个约定成俗的习惯,将声明文件和实现文件名称统一采用“原类名+Category”的方式命名。
调用也非常简单,毫无压力,如下:
首先引入Category的声明文件,然后正常调用即可。
  1. #import "SomeClass+Hello.h"
  2.  
  3. SomeClass * sc =[[SomeClass alloc] init];
  4. [sc hello] 
执行结果是:
nameJacky 
 
Category的使用场景:

1、当你在定义类的时候,在某些情况下(例如需求变更),你可能想要为其中的某个或几个类中添加方法。
2、一个类中包含了许多不同的方法需要实现,而这些方法需要不同团队的成员实现
3、当你在使用基础类库中的类时,你可能希望这些类实现一些你需要的方法。
 
遇到以上这些需求,Category可以帮助你解决问题。当然,使用Category也有些问题需要注意,
1、Category可以访问原始类的实例变量,但不能添加变量,如果想添加变量,可以考虑通过继承创建子类。
2、Category可以重载原始类的方法,但不推荐这么做,这么做的后果是你再也不能访问原来的方法。如果确实要重载,正确的选择是创建子类。
3、和普通接口有所区别的是,在分类的实现文件中可以不必实现所有声明的方法,只要你不去调用它。
 
用好Category可以充分利用Objective-C的动态特性,编写出灵活简洁的代码。
 
—Protocol— 
下面我们再来看Protocol。
Protocol,简单来说就是一系列不属于任何类的方法列表,其中声明的方法可以被任何类实现。这种模式一般称为代理(delegation)模式。你通过Protocol定义各种行为,在不同的场景采用不同的实现方式。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View和Controller的解耦。
 
定义Protocol很简单,在声明文件(h文件)中通过关键字@protocol定义,然后给出Protocol的名称,方法列表,然后用@end表示Protocol结束。在@end指令结束之前定义的方法,都属于这个Protocol。例如:
  1. @protocol ProcessDataDelegate <NSObject>
  2. @required
  3. - (void) processSuccessful: (BOOL)success;
  4.  
  5. @optional
  6. - (id) submitOrder: (NSNumber *) orderid;
  7. @end
以上代码可以单独放在一个h文件中,也可以写在相关类的h文件中,可以视具体情况而定。该Protocol包含两个方法,processSuccessful和submitOrder。这里还有两个关键字,@required和@optional,表示如果要实现这个协议,那么processSuccessful方法是必须要实现的,submitOrder则是可选的,这两个注解关键字是在Objective-C 2.0之后加入的语法特性。如果不注明,那么方法默认是@required的,必须实现。
 
那么如何实现这个Protocol呢,很简单,创建一个普通的Objective-C类,取名为TestAppDelegate,这时会生成一个h文件和m文件。在h文件中引入包含Protocol的h文件,之后声明采用这个Protocol即可,如下:
  1. @interface TestAppDelegate : NSObject<ProcessDataDelegate>;
  2.  
  3. @end
用尖括号(<...>)括起来的ProcessDataDelegate就是我们创建的Protocol。如果要采用多个Protocol,可以在尖括号内引入多个Protocol名称,并用逗号隔开即可。例如<ProcessDataDelegate,xxxDelegate>
 
m文件如下:
  1. @implementation TestAppDelegate
  2.  
  3. - (void) processSuccessful: (BOOL)success{
  4. if (success) {
  5. NSLog(@"成功");
  6. }else {
  7. NSLog(@"失败");
  8. }
  9. }
  10.  
  11. @end 
由于submitOrder方法是可选的,所以我们可以只实现processSuccessful。
 
Protocol一般使用在哪些场景呢?Objective-C里的Protocol和Java语言中的接口很类似,如果一些类之间没有继承关系,但是又具备某些相同的行为,则可以使用Protocol来描述它们的关系。不同的类,可以遵守同一个Protocol,在不同的场景下注入不同的实例,实现不同的功能。其中最常用的就是委托代理模式,Cocoa框架中大量采用了这种模式实现数据和UI的分离。例如UIView产生的所有事件,都是通过委托的方式交给Controller完成。根据约定,框架中后缀为Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate等,使用时大家可以留意一下,体会其用法。
 
使用Protocol时还需要注意的是:
1、Protocol本身是可以继承的,比如:
  1. @protocol A
  2. -(void)methodA;
  3. @end
  4. @protocol B <A>
  5. -(void)methodB;
  6. @end

如果你要实现B,那么methodA和methodB都需要实现。

2、Protocol是类无关的,任何类都可以实现定义好的Protocol。如果我们想知道某个类是否实现了某个Protocol,还可以使用conformsToProtocol进行判断,如下:
  1. [obj conformsToProtocol:@protocol(ProcessDataDelegate)] 
 
好吧,具体的语言特性这次就介绍这么多。从某种意义上来说,Objective-C是一门古老的语言,发明于1980年。1988年,乔布斯的Next公司获得了Objective-C语言的授权,并开发出了Objective-C的语言库和NEXTSTEP的开发环境。NextStep是以Mach和BSD为基础,Objective-C是其语言和运行库,后来的事大家都清楚,苹果买了Next,乔布斯回归苹果,开始神奇的苹果振兴之路,NextStep成了Max OS X的基础。以后发展越来越好,Objctive-C成了Apple的当家语言,现在基本上是Apple在维护Objctive-C的发展。
 
在苹果的AppStore推出之前,Objective-C一直相对小众,但是其优秀的语言特性似乎一直在为后面的爆发积蓄力量,当苹果平台级的应用出现之后,Objective-C开始大放异彩,静态语言的效率和动态语言的特性得到众多程序员的喜爱,目前它已经以火箭般的速度蹿升TIOBE语言排行版第四位。
 
对于喜爱苹果技术的技术人员来说,Objective-C是你必须深入了解和值得学习的一门语言,希望以后有机会多写一些相关的文章。

Objective-C——消息、Category和Protocol的更多相关文章

  1. objective c, category 和 protocol 中添加property

    property的本质是实例变量 + getter 和 setter 方法 category和protocol可以添加方法 category 和 protocol中可以添加@property 关键字 ...

  2. 设计模式之开放-封闭原则(引申出Objective-C中继承、Category、Protocol三者的区别,这点面试常问)

    开放封闭原则(OCP原则The Open-Closed Principle)是面向对象的核心设计所在.它是说,软件开发实体(类.模块.函数等)应该可以扩展,但是不能修改. 这个原则有两个特征,一个是说 ...

  3. Objective中的协议(Protocol)

    Objective中的协议(Protocol) 作用: 专门用来声明一大堆方法. (不能声明属性,也不能实现方法,只能用来写方法的声明). 只要某个类遵守了这个协议.就相当于拥有这个协议中的所有的方法 ...

  4. 04OC之分类Category,协议Protocol,Copy,代码块block

    一.Protocol协议 我们都知道,在C#有个规范称之为接口,就是规范一系列的行为,事物.在C#中是使用Interface关键字来声明一个接口的,但是在OC中interface是用来声明类,所以用了 ...

  5. ios category,protocol理解

    category: 向现有的类中增加方法,同时提供方法的实现,现有类不需要做任何改动. protocol:(相当于Java或C#中的接口interface,当很多类都要需要类似的方法,但是方法具体实现 ...

  6. iOS的category和protocol

    很多时候我们需要扩展一下现有的类,增加一点功能.如果有源码,修改一下即可,如果是第三方的库,就要麻烦一些.在C++中我们使用类继承的方法来实现,在ObjectiveC中当然也可以这么做,不过Objec ...

  7. Category&nbsp;Protocol&nbsp;Exte…

    作者:韩俊强 总结以往方法: 继承是可以完成对类型的扩充,不仅能添加方法,也可以添加实例变量:/缺点:如果工程中有很多需要扩充类的使用,此时要用子类去完成替换,工作量非常大,还容易出错.下面通过例子过 ...

  8. xcode 在哪里新建category、protocol等文件

    1.和以前新建新文件一样.2.当然选IOS啦,不过OS X也有这个选项,然后Objctive-C File. 3.在File Type里选就OK啦.

  9. oc中protocol、category和继承的区别

    OC中protocol.category和继承的区别以前还是有点迷糊,面试的时候说的有点混乱,现在结合一些资料总结一下. 利用继承,多态是一个很好的保持"对扩展开放.对更改封闭"( ...

随机推荐

  1. 流畅的python和cookbook学习笔记(九)

    1.减少可调用对象的参数个数,使用functools.partial冻结参数 使用functools.partial(),可以固定一个或者多个值,减少调用参数. >>> def sp ...

  2. 基于Node.js的ghost开源博客平台

    Ghost 是一套基于Node.js 构建的开源博客平台(Open source blogging platform),具有易用的书写界面和体验. 1.安装node windows 下安装npm:ht ...

  3. flask_sqlalchemy filter 和filter_by的区别

    1. filter需要通过类名.属性名的方式,类名.属性名==值.filter_by 直接使用属性名=值,可以看源码filter_by需要传一个 **kwargs 2. filter支持> &l ...

  4. [C]逗号运算符

    https://baike.baidu.com/item/%E9%80%97%E5%8F%B7%E8%BF%90%E7%AE%97%E7%AC%A6/7959271?fr=aladdin

  5. UNION ALL 视图 'ImprotHIS2012.dbo.ImportHISData' 不可更新,因为没有找到分区依据列。 Severity 16 State 12

    -- 3 更正措施,使约束check一次 Alter Table ImprotHIS_Bak_2011.dbo.ImportHISData with check Check Constraint al ...

  6. Vue-resource和Axios对比以及Vue-axios

    vue更新到2.0之后,作者就宣告不再对vue-resource更新,而是推荐的axios. vue-resource特点 vue-resource插件具有以下特点: 1,体积小 vue-resour ...

  7. Java 文件上传与下载、email

    1. 文件上传与下载 1.1 文件上传 文件上传,要点: 前台: 1. 提交方式:post 2. 表单中有文件上传的表单项: <input type="file" /> ...

  8. 为TextView增加onclick

    必须添加上 android:clickable="true"

  9. switch case 忘记 break

    昨天纠结了很久的问题,一直找不到原因,早上发现是一个低级错误: private void selectButton(int id) { switch (id) { case 0: recommend_ ...

  10. javascprit form表单提交前验证以及ajax返回json

    1.今天要做一个手机验证码验证的功能.需求是前端页面点击发送 短信验证码,后台接收后通过ajax返回到前端,之后前端在提交时候进行验证.思路很简单,不过做的过程还是学到不少的东西. 1.ajax请求后 ...