一、分类
1、什么是分类
分类是为了给某一个类扩充方法,并且不修改原来的类。
2、分类的格式
// 声明
@interface 类名(分类名)
@end
// 分类的实现
@implementaion 类名(分类名)
@end
下面以为Person类添加分类来举例:
先定义一个类Person,下面是它的简单声明和实现:
//
// Person.h
// 04-Category-分类
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)test;
@end
#import "Person.h"
@implementation Person
- (void)test
{
NSLog(@"调用了test方法");
}
@end
下面为Person类定义一个分类,分类名为HR:
//
// Person+HR.h
// 04-Category-分类
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Person.h"
@interface Person (HR)
// 创建一个学习的方法
- (void)study;
@end
#import "Person+HR.h"
@implementation Person (HR)
- (void)study
{
NSLog(@"学习!");
}
@end
主测试程序:
//
// main.m
// 04-Category-分类
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Person+HR.h"
int main(int argc, const char * argv[]) {
Person *p = [[Person alloc] init];
[p test];
[p study];
return 0;
}
运行结果为:
2015-04-03 21:22:14.531 04-Category-分类[676:21021] 调用了test方法
2015-04-03 21:22:14.532 04-Category-分类[676:21021] 学习!
3、分类的好处
(1)一个庞大的类可以分模块开发(2)一个庞大的类可以由多个人来编写,更有利于团队合作
4、分类的使用注意
(1)分类只能增加方法(包括类方法和对象方法),不能增加成员变量(2)在分类方法的实现中可以访问原来类中的成员变量;
(3)分类中可以重新实现原来类中的方法,但是会覆盖掉原来的方法,导致原来的方法无法再使用(警告);
(4)方法调用的优先级:分类->原来的类->父类,若包含有多个分类,则最后参与编译的分类优先;
(5)在很多的情况下,往往是给系统自带的类添加分类,如NSObject和NSString,因为有的时候,系统类可能并不能满足我们的要求。
(6)在大规模的应用中,通常把相应的功能写成一个分类,可以有无限个分类,对原有类进行扩充,一般分模块写,一个模块一个分类。
(7)个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效
5、给NSString类添加类方法
为NSString类添加一个计算字符串中阿拉伯数字个数的方法,所以为NSString添加一个Number分类Category
分类Category的声明:
//
// NSString+Number.h
// 05-分类的应用
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface NSString (Number)
+ (NSUInteger)numberCountOfString:(NSString *)str;
- (NSUInteger)numberCount;
@end
分类Number的实现:
//
// NSString+Number.m
// 05-分类的应用
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "NSString+Number.h"
@implementation NSString (Number)
+ (NSUInteger)numberCountOfString:(NSString *)str
{
// 获取NSString字符串的长度
NSUInteger length = str.length;
// 定义一个NSUInteger变量来接收字符串中阿拉伯数字的个数
NSUInteger count = 0;
// 遍历NSString字符串,取出所有的字符,判断是否是处于'0'和'9'之间
for (NSInteger i = 0; i < length; i++){
unichar c = [str characterAtIndex:i];
if (c >= '0' && c <= '9'){
count++;
}
}
// 获取返回值
return count;
}
- (NSUInteger)numberCount
{
return [NSString numberCountOfString:self];
// NSUInteger length = self.length;
//
// NSUInteger count = 0;
// for (NSUInteger i = 0; i < length; i++){
// unichar c = [self characterAtIndex: i];
// if (c >= '0' && c <= '9')
// count++;
// }
//
// return count;
}
@end
测试主程序:
//
// main.m
// 05-分类的应用
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSString+Number.h"
int main(int argc, const char * argv[]) {
NSString *str = @"hr199202rui05";
NSUInteger count = [NSString numberCountOfString:str];
NSLog(@"%ld", count);
NSLog(@"%ld", [str numberCount]);
return 0;
}
测试结果运行:
//
// main.m
// 05-分类的应用
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSString+Number.h"
int main(int argc, const char * argv[]) {
NSString *str = @"hr199202rui05";
NSUInteger count = [NSString numberCountOfString:str];
NSLog(@"%ld", count);
NSLog(@"%ld", [str numberCount]);
return 0;
}
二、类的本质
1、类的本质
概念:其实类也是一个对象,是Class类型的对象,简称“类对象”
原理:类在内存中只创建一次,相对于内存来说,类是内存里的一个对象,是一个Class类型的对象。
以Person类来举例,一个Class类型的Person类类对象,在执行代码 Person *p = [[Person alloc] init]; 时会创建一个Person类的实例对象。而实例对象,我们知道在内存中可以创建任意多个(只要内存充足)。
Class类型的定义:
Typedef struct obj class *class;
类名就代表着类对象,每个类只有一个类对象。
创建实例对象有两种方式:
(1)利用class 创建 Person类--------------------->Class c = [Person class]; Person *p = [[class alloc] init];
(2)利用Person 创建Person类型的对象-------> Person *p=[[Person alloc] init];
获取类对象有两种方式:
(1)通过类对象来获取:Class c = [Person class];此处使用的是类方法
(2)通过实例对象来获取:Person *p = [[Person alloc] init]; Class c = [p class];此处使用的是对象方法
2、类的加载和初始化
类的加载和初始化涉及到两个类方法:
+load和+initialize
1、+load()
(1)在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法
(2)先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load
(3)先加载元原始类,再加载分类
(4)不管程序运行过程有没有用到这个类,都会调用+load加载
2、+initialize()
(1)在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法
(2)一个类只会调用一次+initialize方法,先调用父类的,再调用子类的
定义一个Person类:
//
// Person.h
// 06-类的本质
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
+ (void)test;
@end
//
// Person.m
// 06-类的本质
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Person.h"
@implementation Person
+ (void)test
{
NSLog(@"Class method test()");
}
// 在类被加载的时候调用,而且只会被加载一次
+ (void)load
{
NSLog(@"Person ---- load");
}
// 此函数当类第一次被使用到时会被调用
+ (void)initialize
{
NSLog(@"Person initialize");
}
@end
定义一个继承Person类的Student类:
//
// Student.h
// 06-类的本质
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Person.h"
@interface Student : Person
@end
//
// Student.m
// 06-类的本质
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Student.h"
@implementation Student
// 在类被加载的时候调用,而且只会被加载一次
+ (void)load
{
NSLog(@"Student ---- load");
}
// 当类第一次被使用时会被调用
+ (void)initialize
{
NSLog(@"Student initialize");
}
@end
定义一个Student的i类GoodStudent类:
//
// GoodStuent.h
// 06-类的本质
//
// Created by rui on 4/4/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Student.h"
@interface GoodStudent : Student
@end
//
// GoodStuent.m
// 06-类的本质
//
// Created by rui on 4/4/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "GoodStudent.h"
@implementation GoodStudent
+ (void)load
{
NSLog(@"Good Student ---- load");
}
+ (void)initialize
{
NSLog(@"GoodStudent initialize");
}
@end
定义一个Person的分类:
//
// Person+Test.h
// 06-类的本质
//
// Created by rui on 4/4/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Person.h"
@interface Person (Test)
@end
//
// Person+Test.m
// 06-类的本质
//
// Created by rui on 4/4/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import "Person+Test.h"
@implementation Person (Test)
+ (void)load
{
NSLog(@"Person (Test) load");
}
+ (void)initialize
{
NSLog(@"Person (Test) initialize");
}
@end
在主测试函数中:
//
// main.m
// 06-类的本质
//
// Created by rui on 4/3/15.
// Copyright (c) 2015 itcast. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Person.h"
#import "Student.h"
#import "GoodStudent.h"
int main(int argc, const char * argv[]) {
// Person *p = [[Person alloc] init];
// Student *stu = [[Student alloc] init];
// [[Person alloc] jnit];
[[GoodStudent alloc] init];
return 0;
}
编译运行函数之后得到:
2015-04-04 00:13:39.266 06-类的本质[1258:70964] Person ---- load
2015-04-04 00:13:39.268 06-类的本质[1258:70964] Student ---- load
2015-04-04 00:13:39.268 06-类的本质[1258:70964] Good Student ---- load
2015-04-04 00:13:39.268 06-类的本质[1258:70964] Person (Test) load
2015-04-04 00:13:39.269 06-类的本质[1258:70964] Person (Test) initialize
2015-04-04 00:13:39.269 06-类的本质[1258:70964] Student initialize
2015-04-04 00:13:39.269 06-类的本质[1258:70964] GoodStudent initialize
(1)当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次;
(2)当第一次使用某个类时,就会调用当前类的+initialize方法;
(3)先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load方法,最后调用分类的+load方法),先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)。
(4)注意:在初始化的时候,如果在分类中重写了+initialize方法,则会覆盖掉父类的。
(5)重写+initialize方法可以监听类的使用情况。