您的位置:首页 > 移动开发 > IOS开发

iOS 分类(category)、扩展(Extension)

2016-03-23 14:27 561 查看
一、简介

Categories: 当原有类的方法不够用时,category可在现有类的基础上添加新的方法(即使在你不知道一个类的源码情况下,也可以向这个类添加扩展的方法);但是,Categories只能添加方法,不能添加实例变量。

优势:

(1)类别能够保证你的实现类和其他的文件区分开,即”可以将类的实现分散到不同的文件里” —– “进行模块化设计”

(2)利用类别来调用私有方法

Extension: iOS中的extension就是匿名的分类,只有头文件没有实现文件,扩展的方法只能在原类中实现。扩展可以添加新的实例变量。例如你扩展NSString,那么你只能在NSString的.m实现(这是不可能的),所以尽量少用扩展。用分类就可以了。

二、实例

Person.h

#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)run;
@end


Person.m

#import "Person.h"
@implementation Person
- (void)run{
NSLog(@"本类run。。。");
}
@end


(1)下面是使用category,给Person增加一个玩的方法

Person+PlayGame.h

#import "Person.h"
@interface Person (PlayGame)
- (void)playLol;
- (void)run;
@end


Person+PlayGame.m

#import "Person+PlayGame.h"

@implementation Person (PlayGame)
- (void)playLol{
NSLog(@"此人在玩游戏。。。");
}
- (void)run{
NSLog(@"playgame run...");
}
@end


(2)使用类扩展

Person.h

#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)run;
@end


Person_work.h

#import "Person.h"

@interface Person ()
{
int _height;
}
- (void)run;
- (void)work;
@end


扩展的方法实现在原类的实现文件中,即Person.m

#import "Person.h"
#import "Person_work.h"
@interface Person()

{
int _age;
}
- (void)run;
@end

@implementation Person
- (void)work{
NSLog(@"%d",_age);
NSLog(@"working。。。");
NSLog(@"height is :%d",_height);
}
- (void)run{
NSLog(@"running ....");
NSLog(@"%d",_age);
}
- (void)testRun{
[self run];
NSLog(@"height is :%d",_height);
}
@end


Categories只能添加方法,不能添加实例变量的讨论

类别中只能添加方法,不能添加实例变量。我们经常看见在类别中这样写:@property (nonatomic, assign) CGFloat x;在这种情况下是不会自动生成实例变量的。这里添加的属性,其实是添加的setter和getter方法。

在Objective-C提供的runtime函数中,确实有一个class_addIvar()函数用于给类添加成员变量,但是文档中特别说明:

This function may only be called after objc_allocateClassPair and before objc_registerClassPair. Adding an instance variable to an existing class is not supported.

意思是说,这个函数只能在“构建一个类的过程中”调用。一旦完成类定义,就不能再添加成员变量了。经过编译的类在程序启动后就runtime加载,没有机会调用addIvar。程序在运行时动态构建的类需要在调用 objc_allocateClassPair 之后,objc_registerClassPair之前才可以被使用,同样没有机会再添加成员变量。那为什么可以在类别中添加方法和属性呢?

因为方法和属性并不“属于”类实例,而成员变量“属于”类实例。我们所说的“类实例”概念,指的是一块内存区域,包含了isa指针和所有的成员变量。所以假如允许动态修改类成员变量布局,已经创建出的类实例就不符合类定义了,变成了无效对象。但方法定义是在objc_class中管理的,不管如何增删类方法,都不影响类实例的内存布局,已经创建出的类实例仍然可正常使用。

在类别中添加实例变量

那我偏偏想要在类别中添加实例变量该怎么办呢?这时候就要用到runtime了,不要忘记了Objective-C是动态语言。一种常见的办法是通过runtime.h中objc_getAssociatedObject / objc_setAssociatedObject来访问和生成关联对象。这两个方法可以让一个对象和另一个对象关联,就是说一个对象可以保持对另一个对象的引用,并获取那个对象。

//NSObject+IndieBandName.h
@interface NSObject (IndieBandName)
@property (nonatomic, strong) NSString *indieBandName;
@end
//上面是头文件声明,下面的实现的.m文件:

// NSObject+IndieBandName.m
#import "NSObject+Extension.h"
#import <objc/runtime.h>
static const void *IndieBandNameKey = &IndieBandNameKey;
@implementation NSObject (IndieBandName)
@dynamic indieBandName;
- (NSString *)indieBandName {
return objc_getAssociatedObject(self, IndieBandNameKey);
}
- (void)setIndieBandName:(NSString *)indieBandName {
objc_setAssociatedObject(self, IndieBandNameKey, indieBandName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end


通过runtime的两种方法就可以为类别添加一个实例变量了。

美团技术团的一篇博文:http://tech.meituan.com/DiveIntoCategory.html

Categories官方文档

Categories Add Methods to Existing Classes

如果你需要向现有的类中添加一个方法,最简单的方式是使用Category

[1]Category

1.语法

使用@interface声明一个category,不需要指明任何继承,但需要在括号中指明category的名字。

例如:

@interface ClassName (CategoryName)

@end

注意:(1)你可以为任何一个类声明一个分类,即使你没有原始实现的源代码。你在类别中声明的任何方法对于原始类的所有实例都可以使用

(2)Once you’ve declared a category and implemented the methods, you can use those methods from any instance of the class, as if they were part of the original class interface

例子:一个XYZPerson类,有一个人的first name、 last name。你需要频繁地显示他们。为了不用每次写代码获得first name、 last name。你可以为XYZPerson类增加一个分类。一个分类通常被声明在一个单独的.h文件中,而实现在另外的源代码文件中。在XYZPerson中,你在.h文件中声明的分类叫做XYZPerson+XYZPersonNameDisplayAdditions.h。(使用分类时需要导入分类的头文件)

头文件

`#import “XYZPerson.h”

@interface XYZPerson (XYZPersonNameDisplayAdditions)

(NSString *)lastNameFirstNameString;

@end

//例子中XYZPersonNameDisplayAdditions分类定义了一个返回需要的字符串的额外的方法。

实现:
#import “XYZPerson+XYZPersonNameDisplayAdditions.h”

@implementation XYZPerson (XYZPersonNameDisplayAdditions)

(NSString *)lastNameFirstNameString {

return [NSString stringWithFormat:@”%@, %@”, self.lastName, self.firstName];

}

@end`

2.category使用

(1)use categories to split(分离)the implementation of a complex class across multiple source code files.

(2)to provide different implementations for the category methods, depending on whether you were writing an app for OS X or iOS.

3.限制

在分类中不能声明额外的实例变量

[2]扩展

Class Extensions Extend the Internal Implementation

1.介绍: A class extension bears some similarity to a category, but it can only be added to a class for which you have the source code at compile time (the class is compiled at the same time as the class extension). The methods declared by a class extension are implemented in the @implementation block for the original class so you can’t, for example, declare a class extension on a framework class, such as a Cocoa or Cocoa Touch class like NSString.

头文件

例1:@interface ClassName ()

@end

//因为括号中没有名字,所以extension进程称为匿名分类

例2:类扩展能增加自己的属性和实例

@interface XYZPerson ()

@property NSObject *extraProperty;

@end

2.使用使用类扩展去隐藏私有信息

例如XYZPerson中可能有一个uniqueIdentifier的属性,用来记录社保号的信息,给一个人分配一个社保号可能需要大量的工作,因此XYZPerson需要又一个readonly属性。而且提供一些方法。

@interface XYZPerson : NSObject



@property (readonly) NSString *uniqueIdentifier;

(void)assignUniqueIdentifier;

@end

//这意味着uniqueIdentifier是不能改变的,如果一个人没有社保号,就调用- (void)assignUniqueIdentifier方法来获得。

为了XYZPerson类能内部的改变这个属性,它需要在类扩展中重新定义这个属性,即在implementation文件上方

@interface XYZPerson ()

@property (readwrite) NSString *uniqueIdentifier;

@end

@implementation XYZPerson



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