标签:
原文:http://rypress.com/tutorials/objective-c/categories
分类可用让一个类定义到不同的文件中。如果这个类非常大的话讲类代码分散模块化将更有助于代码的管理。
Using multiple files to implement a class
在这一章中,我们将使用OC的category(分类)功能去扩展某个存在的类而不修改任务已经存在的代码。我们也将学习当如何使用分类去模拟一个受保护的方法。扩展特性和分类特性有一些相似所以我们也将简单的介绍一个扩展特性。
在我们测试分类特性之前我们需要准备一个基础的类。新建或者修改Car类的接口文件如下:
// Car.h #import <Foundation/Foundation.h> @interface Car : NSObject @property (copy) NSString *model; @property (readonly) double odometer; - (void)startEngine; - (void)drive; - (void)turnLeft; - (void)turnRight; @end
对应的实现文件如下:
// Car.m #import "Car.h" @implementation Car @synthesize model = _model; - (void)startEngine { NSLog(@"Starting the %@‘s engine", _model); } - (void)drive { NSLog(@"The %@ is now driving", _model); } - (void)turnLeft { NSLog(@"The %@ is turning left", _model); } - (void)turnRight { NSLog(@"The %@ is turning right", _model); } @end
现在,我们如果想要增加一个Car类的方法应该怎么办呢?我们不去修改Car.h和Car.m文件我们使用一个专用的category文件来达到这个目的。
【xf-xrh-xf译注:不同版本的xcode的导航对话框可能存在差异】
分类很像普通的类,也分为接口是实现。在xcodeiOS > Cocoa Touch导航栏下选择category项目,然后输入分类名称和对于哪一个类建立分类,具体如下:
Creating the Maintenance
category
对于分类唯一的限制就是对于同一个类的分类名称不能重复。在命名规则上分类的名称一般是类名和分类名使用加号分隔的写法,在上例中你将会建立Car+Maintenance.h和Car+Maintenance.m两个文件。
在Car+Maintenance.h头文件中,一个分类的头文件内容和普通类的头文件内容很相似,除了在类名右边括号中标示了分类的名称。好了,我们接着在分类头文件中增加一些方法:
// Car+Maintenance.h #import "Car.h" @interface Car (Maintenance) - (BOOL)needsOilChange; - (void)changeOil; - (void)rotateTires; - (void)jumpBatteryUsingCar:(Car *)anotherCar; @end
在运行时,这些方法将会视为Car类的一部分,即使他们被定义在不同的文件中,你可以使用这些方法和被定义在类头文件中的方法没有任何差别。
当然,在使用分类头文件中的方法钱你需要实现这些方法。分类实现文件看起来其实和普通的类实现文件格式内容没有什么差别,唯一的差别就是类名右边括号中将分类名称列出:
// Car+Maintenance.m #import "Car+Maintenance.h" @implementation Car (Maintenance) - (BOOL)needsOilChange { return YES; } - (void)changeOil { NSLog(@"Changing oil for the %@", [self model]); } - (void)rotateTires { NSLog(@"Rotating tires for the %@", [self model]); } - (void)jumpBatteryUsingCar:(Car *)anotherCar { NSLog(@"Jumped the %@ with a %@", [self model], [anotherCar model]); } @end
另外一个非常重要的事情是分类可以重写已有类中的方法(eg:上述Car中的drive方法),但是你不应该这么做。应为分类在OC中是一种扁平结构,如果你想在某个分类中重写某方法而后又想在另外一个分类中再次重写此方法这样就会造成混乱。如果需要重写方法那么当做子类来处理会更好。
任何想要使用分类都需要引入分类的头文件,这和普通类的使用方法是一样的。引入分类头文件后所有再分类中定义的方法都可通过Car类或者其实例来使用了:
// main.m #import <Foundation/Foundation.h> #import "Car.h" #import "Car+Maintenance.h" int main(int argc, const char * argv[]) { @autoreleasepool { Car *porsche = [[Car alloc] init]; porsche.model = @"Porsche 911 Turbo"; Car *ford = [[Car alloc] init]; ford.model = @"Ford F-150"; // "Standard" functionality from Car.h [porsche startEngine]; [porsche drive]; [porsche turnLeft]; [porsche turnRight]; // Additional methods from Car+Maintenance.h if ([porsche needsOilChange]) { [porsche changeOil]; } [porsche rotateTires]; [porsche jumpBatteryUsingCar:ford]; } return 0; }
分类不止被用于类的多文件管理。它也是内部文件组织的一大利器,可以通过它来达到一些技术上某些特性的实现。比如受保护的方法。
还记得在方法这个章节中偶们提到受保护的方法其实在OC中是不存在的;但是你可以使用分类的一些机制去模拟出来这种效果。如果你想定义一些受保护的方法那么需要将这些方法定义在独立的分类中,并且将这个分类的头文件值只被子类的实现文件引用。这样这些方法就只对子类可见,但是对于其他外部是不可见的,例如:
// Car+Protected.h #import "Car.h" @interface Car (Protected) - (void)prepareToDrive; @end // Car+Protected.m #import "Car+Protected.h" @implementation Car (Protected) - (void)prepareToDrive { NSLog(@"Doing some internal work to get the %@ ready to drive", [self model]); } @end
Protected分类中定义了一个方法,这个方法设计为用于类本身和其子类中。要想达到这样的受保护效果,我们需要修改Car.m的drive方法去使用prepareToDrive方法:
// Car.m #import "Car.h" #import "Car+Protected.h" @implementation Car ... - (void)drive { [self prepareToDrive]; NSLog(@"The %@ is now driving", _model); } ...
接下来看一下他是在子类Coupe中如何实现受保护的特性的。在子类的接口文件中没有做什么特别处理,但是在实现文件中将“受保护”方法分类的头文件Car+Protected.h引入。这样就使得prepareToDrive在子类中可见,你甚至可以在Coupe.m中重写这个受保护方法。
// Coupe.h #import "Car.h" @interface Coupe : Car // Extra methods defined by the Coupe subclass @end // Coupe.m #import "Coupe.h" #import "Car+Protected.h" @implementation Coupe - (void)startEngine { [super startEngine]; // Call the protected method here instead of in `drive` [self prepareToDrive]; } - (void)drive { NSLog(@"VROOOOOOM!"); } @end
这里的关键一点是不要在其他非子类实现文件中引入 Car+Protected.h,下下面的例子中你可以观察到prepareToDrive可以被
[ford drive]
and[porsche startEngine]调用的时候使用但是如果你直接调用的话就会报错。
// main.m #import <Foundation/Foundation.h> #import "Car.h" #import "Coupe.h" int main(int argc, const char * argv[]) { @autoreleasepool { Car *ford = [[Car alloc] init]; ford.model = @"Ford F-150"; [ford startEngine]; [ford drive]; // Calls the protected method Car *porsche = [[Coupe alloc] init]; porsche.model = @"Porsche 911 Turbo"; [porsche startEngine]; // Calls the protected method [porsche drive]; // "Protected" methods have not been imported, // so this will *not* work // [porsche prepareToDrive]; SEL protectedMethod = @selector(prepareToDrive); if ([porsche respondsToSelector:protectedMethod]) { // This *will* work [porsche performSelector:protectedMethod]; } } return 0; }
另外请注意,prepareToDrive依然可以通过performSelector:方法被直接调用到。这里再次说明,所有的在OC中的方法都是public的,事实上是没有办法真正隐藏起来的。分类只不过是使用到一些代码组织上的技巧实现了类似的功能。
扩展和分类很相似,扩展不需要去定义独立的接口和实现文件,这些东西需要在类的主实现文件中直接被定义。
还记得如何去实现 私有方法的技巧吗?实现私有方法的时候我们将方法直接写在实现文件中而不再接口文件中做登记。当你定义少量私有方法的时候没有问题,但是如果在大的类中此种方式就会显得非常笨重,扩展可以解决此问题。【xf-xrh-xf译注:扩展其实就是定义私有方法和私有变量的标准做法,这里的意思不是说可以减少代码量而是说通过这样的组织形式使得私有和公有更加区别的清楚。】
例如,如果你想在Car类中正式的定义一个私有方法:engineIsWorking,你就可以在Car.m中包含扩扎来达到此目的。扩展的语法很像一个匿名分类:
// Car.m #import "Car.h" // The class extension @interface Car () - (BOOL)engineIsWorking; @end // The main implementation @implementation Car @synthesize model = _model; - (BOOL)engineIsWorking { // In the real world, this would probably return a useful value return YES; } - (void)startEngine { if ([self engineIsWorking]) { NSLog(@"Starting the %@‘s engine", _model); } } ... @end
除了定义正式的私有方法外,扩展也可以被用在属性的定义上,甚至可以冲重定义接口文件中的属性。这样被重定义的属性在本类内部是被重定义时候的属性参数而在其他文件中保持在接口中定义的属性参数不变。如下面的odometer属性被重定义了,它在本类中有read-write属性参数,但是在其他文件中却依然保持原有的read-only属性参数:
// Car.m #import "Car.h" @interface Car () @property (readwrite) double odometer; - (BOOL)engineIsWorking; @end ...
我们在本类的内部赋值给self.odometer但是要是在其他地方这么做的话就直接报错了。
再次强调扩展有时候在小的类中不是太有用,但是在大的类很多代码的时候就非常有用,私有属性和方法就和其他方法区别的非常清楚。
扩展Xcode4.3版本前被广泛使用,因为私有方法在被使用前必须先声明,这样大家都要使用的话就非常不方便,所以扩展就提供了这种预声明的方式来解决这个问题,你可能没有使用扩展特性,但是我能保证在你的OC生涯中迟早遇到。
本章覆盖了OC的分类和扩展。分类是大类分文件的方法。扩展提供了形似的功能但是实现代码都需要在主实现文件中实现。
除了管理大的类库外,另外一个广泛用途就是使用分类去对内部方法进行方法的补充如NSString
or NSArray。这样做的优势在于你不需要使用子类不需要改变已有的任何代码。但是你必须注意千万不要去重写任何已经存在的方法。
在接下来的章节中,我们将探索另外一个代码组织神器:blocks,块可以向目标传输一段特定的语句,它开启了一个全新的编程方式。
标签:
原文地址:http://www.cnblogs.com/ios123/p/4940130.html