您的位置:首页 > 移动开发 > Objective-C

Objective-C类别(category)和扩展(Extension)的基本概念

2015-02-28 17:27 489 查看
文章来源:/article/1425954.html

/article/1564416.html

category 是Objective-C 里面最常用到的功能之一。category 可以为已经存在的类增加方法,而不需要增加一个子类。而且,我们可以在不知道某个类内部实现的情况下,为该类增加方法。如果我们想增加某个框架(framework)中的类的方法,category 就非常有效。比如,如果想在NSString 上增加一个方法来判断它是否是有效的 URL,那么就可以这样做:

[java] view
plaincopyprint?

@interface NSString (hello)

- (BOOL) isURL;

@end

是不是觉得与类的定义非常像,确实,就是category 没有父类,而且后面要跟括号里面写category 的名字,名字可以随便取。下面是刚刚 isURL 的实现:

[java] view
plaincopyprint?

@implementation NSString(hello)

- (BOOL) isURL{

if( [self hasPrefix:@"http://"] )

return YES;

else

return NO;

}

@end

现在就可以在任何NSString类对象上调用这个方法了。下面是一个调用的例子:

[java] view
plaincopyprint?

NSString* str1 = @"http://www.blog.csdn.net/iukey";

NSString* str2 = @"刘伟Lewis";

if([str1 isURL])

NSLog(@"str1 is a URL");

if([str2 isURL])

NSLog(@"str2 is a URL");

通过上面的例子可以看出,通过类别所添加的新方法就成为类的一部分。我们通过为类别所添加的方法也存在于他的方法列表中,而为NSstring 子类添加的新方法,NSString是不具有的。通过类别所添加的新方法可以向这个类的其他方法一样完成任何操作。在运行时,新添加的方法和已经存在的方法在使用上没有任何区别。通过类别添加的方法和别的方法一样会被他的子类所继承。

类别接口的的定义看起来很像类接口的定义,而不同的是类别名用圆括号列出,他们位于类名后面。类别必须导入他所扩展的类的接口文件。标准语发格式如下:

[java] view
plaincopyprint?

#import "类名.h"

@interface 类名(类别名)

//新方法的声明

@end

和类一样类别的实现文件也要导入它的接口文件。一个常用的命名约定是,类别的基本文件名是这个类别扩展的类的名字后面跟类别名。因此一个名字为 “类名”+“类别名”+“.m”的实现文件看起来就是这样:

[java] view
plaincopyprint?

#import "类名类别名.h"

@interface 类名(类别名)

//新的实现方法

@end

注意:类别并不能为类声明新的实例变量,他只包含方法。然而在类作用域内所有实例变量,都能被这些类别访问。他们包括为类声明的所有的实例变量,甚至那些被@private 修饰的变量。可以为一个类添加多个类别,但每个类别名必须不同,而且每个类别都必须声明并实现一套不同的方法。

要记住,我们通过 category 来修改一个类的时候,他对应应用程序里这个类所有对象都起作用。跟子类不一样,category 不能增加成员变量。我们还可以用 category里重写类原先存在的方法(但是并不推荐这么做)。最后给出一个完整在例子(这个例子是扩展UIImage类 为其添加一个把图像变为灰度图像的方法):

[java] view
plaincopyprint?

// GrayScale.h

// XOGameFrame

//

// Created by song on 11-1-12.

// Copyright 2011 __MyCompanyName__. All rights reserved.

//

#import <Foundation/Foundation.h>

@interface UIImage (grayscale)

- (UIImage *)convertToGrayscale ;

@end

[java] view
plaincopyprint?

//

// GrayScale.m

// XOGameFrame

//

// Created by song on 11-1-12.

// Copyright 2011 __MyCompanyName__. All rights reserved.

//

#import "GrayScale.h"

@implementation UIImage (grayscale)

typedef enum {

ALPHA = 0,

BLUE = 1,

GREEN = 2,

RED = 3

} PIXELS;

- (UIImage *)convertToGrayscale {

CGSize size = [self size];

int width = size.width;

int height = size.height;

// the pixels will be painted to this array

uint32_t *pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t));

// clear the pixels so any transparency is preserved

memset(pixels, 0, width * height * sizeof(uint32_t));

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

// create a context with RGBA pixels

CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace,

kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);

// paint the bitmap to our context which will fill in the pixels array

CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]);

for(int y = 0; y < height; y++) {

for(int x = 0; x < width; x++) {

uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x];

// convert to grayscale using recommended method: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE];

// set the pixels to gray

rgbaPixel[RED] = gray;

rgbaPixel[GREEN] = gray;

rgbaPixel[BLUE] = gray;

}

}

// create a new CGImageRef from our context with the modified pixels

CGImageRef image = CGBitmapContextCreateImage(context);

// we're done with the context, color space, and pixels

CGContextRelease(context);

CGColorSpaceRelease(colorSpace);

free(pixels);

// make a new UIImage to return

UIImage *resultUIImage = [UIImage imageWithCGImage:image];

// we're done with image now too

CGImageRelease(image);

return resultUIImage;

}

@end

1、分类(category)

使用Object-C中的分类,是一种编译时的手段,允许我们通过给一个类添加方法来扩充它(但是通过category不能添加新的实例变量),并且我们不需要访问类中的代码就可以做到,这点和javascript中使用原型来定义属性有点类似。

我们可以为一个类创建一个新的方法,而不需要在代码中编辑类定义。

下面就是定义并使用分类的例子程序,通过下面代码,我们可以给Object-C中的NSString 添加camelCaseString分类,使用camelCaseString方法,就可以去掉一个字符串中的空格,并将原有空格后的单词改写成大写(即将字符串转化为驼峰式)。

[cpp] view
plaincopy





#import <Foundation/Foundation.h>

/*

定义分类的过程大致可分为以下几个步骤:

第一步、创建一个带有接口的新文件,即创建已有类

第二步、在新文件中添加需要扩展的方法及方法的实现,即需要添加的分类

*/

//NSString 表示将要添加分类的类名称,该类必须是已存在的。

//CamelCase 是为类添加的方法名称。

//只能添加方法,不能添加变量。

//头文件命名惯例:ClassName+CategoryName.h

@interface NSString (CamelCase)

-(NSString*) camelCaseString;

@end

@implementation NSString (CamelCase)

-(NSString*) camelCaseString

{

//调用NSString的内部方法获取驼峰字符串。

//self指向被添加分类的类。

NSString *castr = [self capitalizedString];

//创建数组来过滤掉空格, 通过分隔符对字符进行组合。

NSArray *array = [castr componentsSeparatedByCharactersInSet:

[NSCharacterSet whitespaceCharacterSet]];

//把数组的字符输出

NSString *output = @"";

for(NSString *word in array)

{

output = [output stringByAppendingString:word];

}

return output;

}

@end

int main (int argc, const char * argv[])

{

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSString *str = @"My name is bill.";

NSLog(@"%@", str);

str = [str camelCaseString];

NSLog(@"%@", str);

[pool drain];

return 0;

}


2、扩展(Extension)

关于扩展,你可以这样理解:扩展是一种匿名分类;但是和匿名分类不一样的是,扩展可以添加新的实例变量(是不是觉得扩展已经强大到非一般的地步了? ^_^)

从Xcode 4 之后就推荐在自定义类的.m文件中使用扩展,这样就能保证良好的代码封装性,避免把私有接口暴露给外面。

下面是一个扩展的例子:

[plain] view
plaincopy





@interface MyClass : NSObject

- (float)value;

@end

@interface MyClass () { //注意此处:扩展

float value;

}

- (void)setValue:(float)newValue;

@end

@implementation MyClass

- (float)value {

return value;

}

- (void)setValue:(float)newValue {

value = newValue;

}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: