从C#到Objective-C,按部就班学习苹果开发(3)--分类(category)和协议Protocal的理解

本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,但愿带给你们更好,更真实的转换历程体验。本文继续上一篇随笔《从C#到Object C,按部就班学习苹果开发(2)--Objective-C和C#的差别》,继续对比介绍它们二者之间的差别,以便咱们从C#阵营过来的人员加深印象,深刻了解Objective-C语言的特性。本篇随笔主要针对Objective-C里面的分类(category)和协议Protocal概念的理解进行介绍。html

一、分类(category)概念和使用

若是咱们使用过C#,咱们都知道,C#里面有一个叫作扩展函数的东西,能够在不继承已有类的状况下,给存在的类增长一些本来没有的接口函数,Objective-C的分类概念和这个很类似,甚至能够说是同一类型的东西,虽然不知道他们谁先谁后出现,这个东西的引入,能使得编程方面更加丰富高效。编程

Objective-C提供了一种不同凡响的方式——Category,能够动态的为已经存在的类添加新的行为。这样能够保证类的原始设计规模较小,功能增长时再逐步扩展。使用Category对类进行扩展时,不须要访问其源代码,也不须要建立子类。Category使用简单的方式,实现了类的相关方法的模块化,把不一样的类方法分配到不一样的分类文件中。不过Category并不能给类扩展出属性,这点要注意,由于Object C不支持这样的属性扩展。框架

分类(Category)的定义语法以下所示。模块化

@interface ClassName (CategoryName)
 
@end

这里好像它们还有一个约定俗成的习惯,将声明文件和实现文件名称统一采用“原类名+Category”的方式命名。因此OC的这种功能虽然和C#功能差很少,可是这点约定和C#不同,C#无论你放到哪里都行,可是咱们仍是会应该尊重它的规则。函数

例如,咱们给XYZPerson类增长一个扩展方法的定义以下所示,这个定义的函数约定是放到文件"XYZPerson+XYZPersonNameDisplayAdditions.h"里面。学习

#import "XYZPerson.h"
 
@interface XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)testMethod;
@end

那么它的实现代码以下所示,它的代码约定是放到 "XYZPerson+XYZPersonNameDisplayAdditions.m"里面。ui

#import "XYZPerson+XYZPersonNameDisplayAdditions.h"
 
@implementation XYZPerson (XYZPersonNameDisplayAdditions)
- (NSString *)testMethod {
    return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName];
}
@end

在C#里面,扩展方法是命名空间相关的,一旦跳出了命名空间的范围,这个扩展函数就不在起做用,而Objective-C的这个和类同样,没有命名空间的概念,所以在扩展的时候,须要当心谨慎一点,不然容易致使分类的接口和类自己发生冲突。基于这个缘由,因此苹果建议也是给分类的接口增长一个前缀,命名则采用接口的一向规则,以下面代码所示。this

@interface NSSortDescriptor (XYZAdditions)
+ (id)xyz_sortDescriptorWithKey:(NSString *)key ascending:(BOOL)ascending;
@end

这样扩展方法名称虽然长了一点,可是基本上确保和普通的接口方法不会发生冲突了。spa

Category的使用场景:设计

一、当你在定义类的时候,在某些状况下(例如需求变动),你可能想要为其中的某个或几个类中添加方法。
二、一个类中包含了许多不一样的方法须要实现,而这些方法须要不一样团队的成员实现
三、当你在使用基础类库中的类时,你可能但愿这些类实现一些你须要的方法。
 
遇到以上这些需求,Category能够帮助你解决问题。固然,使用Category也有些问题须要注意,
一、Category能够访问原始类的实例变量,但不能添加变量,若是想添加变量,能够考虑经过继承建立子类。
二、Category能够重载原始类的方法,但不推荐这么作,这么作的后果是你不再能访问原来的方法。若是确实要重载,正确的选择是建立子类。
三、和普通接口有所区别的是,在分类的实现文件中能够没必要实现全部声明的方法,只要你不去调用它。

还有一种成为类扩展的功能,它是针对存在代码的类的状况,也就是你的类代码和你扩展的源码是同时编译的状况下。

类扩展的方法和上面的分类相似,他们不须要写扩展分类的名称,这个有点像匿名扩展分类的概念了,以下所示

@interface ClassName ()
 
@end

这个匿名的扩展分类,和普通的Category不一样,它除了能够方法外,还能够添加属性或者变量的。

 

二、协议Protocal

这个概念有很大程度上和C#的接口相似,可是它有所不一样,它能够可选的实现接口@optional,也有必选的实现接口@required,虽然Objective-C里面已经有一个关键字 @interface,不过这个和Protocal仍是有不一样的。

和C#的接口同样,这种协议也能够继承自另一个Protocal,也就是他们能够有继承关系。

@protocol NewProtocal   <Protocal>
@end

因为Objective-C开发的不少应用,如IOS的应用,他们在MVC的开发模型里面,都大量使用了代理模式,这种Protocal很好的处理了这种关系。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View和Controller的解耦。

例如UIView产生的全部事件,都是经过委托的方式交给Controller完成。根据约定,框架中后缀为Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate等。

在C#里面有不少如IClonable, IEnumerable这样的接口,只要实现了,就能实现克隆和枚举,在Objective-C里面,这个就是可使用Protocal来替代了,若是某个协议继承了NSObject,那么这是表明在此声明的协议,是NSObject协议的衍生协议(不是NSObject类),也就是说,这里的语境理解NSObject是一个协议,若是是在@Interface里面的继承关系,那么那个就是NSObject对象。有点意思哦。

下面是一个可选和必选的协议定义例子。

@protocol XYZPieChartViewDataSource
- (NSUInteger)numberOfSegments;
- (CGFloat)sizeOfSegmentAtIndex:(NSUInteger)segmentIndex;
@optional
- (NSString *)titleForSegmentAtIndex:(NSUInteger)segmentIndex;
- (BOOL)shouldExplodeSegmentAtIndex:(NSUInteger)segmentIndex;
@required
- (UIColor *)colorForSegmentAtIndex:(NSUInteger)segmentIndex;
@end

因为协议有可选和必选,若是咱们想知道某个动态的对象是否具备某个接口函数,就是经过@selector操做符来进行判断的。

NSString *thisSegmentTitle;
    if ([self.dataSource respondsToSelector:@selector(titleForSegmentAtIndex:)]) {
        thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
    }

和C#的接口定义相似,Objective-C的一个类对象它能够实现多的协议,以下例子是一个类的接口定义实现几个协议的状况。

@interface MyClass : NSObject <MyProtocol, AnotherProtocol, YetAnotherProtocol>
...
@end

这样就实现了MyClass对象只有一个基类对象,可是能够实现多个协议(C#是多个接口)的状况。

相关文章
相关标签/搜索