---------------------- Java培训、Android培训、iOS培训、.Net培训,期待与您交流! ---------------------
一、分类Category
1. 分类的格式
* 分类的声明
@interface 类名 (分类名称)
// 方法的声明
@end
* 分类的实现
@implementation 类名 (分类名称)
// 方法的实现
@end
2. 分类的使用注意:
1》分类只能增加方法,不能增加成员变量;
2》分类方法实现中,可以访问原来类中声明的成员变量;
3》分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法(分类的优先级最高),导致原来的方法失效;
4》方法调用的优先级:分类(最后参与编译的分类才会有效) > 原来的类 > 父类。
/* 给NSString类添加分类*/
#import <Foundation/Foundation.h>
@interface NSString (NumberCount)
+ (int)numberCountOfString:(NSString *)str;
- (int)numberCount;
@end
@implementation NSString (NumberCount)
+ (int)numberCountOfString:(NSString *)str
{
// 1.定义变量计算数字的个数
int count = 0;
// 2.遍历
for (int i=0; i<str.length; i++) {
unichar c = [str characterAtIndex:i];
if (c>=48 && c<=57) {
count++;
}
}
return count;
}
- (int)numberCount
{
// int count = 0;
// for (int i=0; i<self.length; i++) {
// unichar c = [self characterAtIndex:i];
// if (c>='0' && c<='9') {
// count++;
// }
// }
// return count;
return [NSString numberCountOfString:self];
}
@end
3. 分类和继承
分类和继承都是在不改变原来模型的情况下,给类扩充一些方法。
二、类的本质
1. 类对象
类也是一个对象,是Class类型的对象,简称“类对象”。
类对象的本质:typedef struct objc_class *Class;
类名就代表类对象,一个类只能有一个类对象。
Person *p = [[Person alloc] init];
Class c = [p class];
// 类名代表类对象 类对象==类
[c test]; // [Person test];
// 利用Person这个类创建了2个Person类型的对象
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
// 类本身也是一个对象,是个Class类型的对象,简称类对象
/*
利用Class 创建 Person类对象(类对象)
利用Person类对象 创建 Person类型的对象(实例对象)
*/
// 获取内存中的类对象
// 1.方式一
Class c1 = [p1 class];
Class c2 = [p2 class];
// 2.方式二
Class c3 = [Person class];
NSLog(@"c1=%p, c2=%p, c3=%p", c1, c2, c3); // 相同类对象
2. +load和+initialize
+ load
当程序启动时,系统就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次。
加载顺序:先加载父类,在加载子类,最后加载分类。
不管程序有没有用到这个类,都会调用+load这个方法把类加载进来。
+ initialize
当第一次使用某个类时,就会调用当前类的+initialize方法,只会调用一次。
调用顺序:先调用(初始化)父类的,再调用(初始化)子类。如果有分类,则分类会替换原来的类来进行初始化。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
+ (void)test;
@end
@implementation Person
+ (void)test
{
NSLog(@"调用了test类方法");
}
// 当程序启动的时候,就会加载一次项目中所有的类,类加载完毕后就会调用+load方法
// 先加载父类,再加载子类
+ (void)load
{
NSLog(@"Person load");
}
// 当第一次使用这个类的时候,就会调用一次initialize方法
+ (void)initialize
{
// 监听
NSLog(@"Person initialize");
}
@end
三、NSLog输出特性和SEL对象
1. description方法
1》-description方法
Person *p = [[Person alloc] init];
p.age = 20;
p.name = @"jack";
// 默认情况下,利用NSLog和%@输出对象时,结果是:<类名: 内存地址>
// 1.会调用对象p的-description方法
// 2.拿到-description方法的返回值(NSString *),显示到屏幕上
// 3.-description方法默认返回的时"类名+内存地址"
NSLog(@"%@", p);
2》+description方法
Class c = [Person class];
// 默认情况下,利用NSLog和%@输出类对象时,结果是:类名
// 1.会调用类的+description方法
// 2.拿到+description方法的返回值(NSString *),显示到屏幕上
// 3.+description方法默认返回的时"类名"
NSLog(@"%@", c);
3》重写description方法
打印对象的时候用:
// 决定了实例对象的输出结果
- (NSString *)description
{
// NSLog(@"%@", self); // 死循环
return [NSString stringWithFormat:@"age=%d, name=%@", _age, _name];
}
// 决定类对象的输出结果
+ (NSString *)description
{
return NSStringFromClass([self class]);
}
2. SEL对象
每个类的方法列表都封装在类对象中,每个方法都有一个与之对应的SEL类型的对象,根据这个SEL对象就能找到方法的地址,然后调用方法。
对象放消息,就是发SEL。
SEL类型的本质:typedef struct objc_selector *SEL;
1》SEL对象的创建
SEL s1 = @selector(方法名);
// 【NSString对象转换为SEL对象】
SEL s2 = NSSelectorFromString(@"方法名"); // 知道方法名字符串 ==> 调用该方法,用SEL类型数据接收字符串,再调用方法
2》SEL对象的使用
// 【将SEL对象转换为NSString对象】
NSString *str = NSStringFromSelector(@selector(方法名));
// 调用对象p的test方法:
Person *p = [[Person alloc] init];
[p performSelector:@selector(test)];
NSString *str = @"12345";
[p test3:str];
[p performSelector:@selector(test3:) withObject:str]; // performSelector方法可以有参数
间接调用对象方法:
* 首先会把test2包装成SEL类型的数据 (创建SEL数据:@selector(test2))
* 根据SEL数据找到对应的方法地址
* 根据方法地址调用对应的方法
3》_cmd
_cmd 代表当前方法的SEL,每个方法内部都有这么一个变量。
- (void)test2
{
// 死循环
// [self performSelector:_cmd];
// _cmd 代表当前方法的SEL,每个方法内部都有这么一个变量..
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"test2.., _cmd=%@", str);
}
3. NSLog输出增强
NSLog(@"%d", __LINE__); // 代码当前行号
// NSLog输出C语言字符串的时候,不能有中文
// NSLog(@"%s", __FILE__); // 输出不成功!(可以先做转换)
printf("%s\n", __FILE__); // 当前文件路径
NSLog(@"%s", __func__); // 当前函数名
四、类名、对象名、方法名的输出
1. 输出类名
@implementation Person
// + (NSString *)description
// {
// return NSStringFromClass([self class]);
// }
@end
int main()
{
Class c = [Person class];
NSLog(@"%@", c);
}
2. 输出对象
我们可以用类名和对象的地址(不是对象指针的地址),来表达一个什么类型的什么对象。
@implementation Person
// - (NSString *)description
// {
// return self;
// }
@end
int main()
{
Person *p = [[Person alloc] init];
NSLog(@"%@", p);
}
3. 输出方法名
@interface Person : NSObject
- (NSString)fangfaming1;
+ (NSString)fangfaming2;
@end
@implementation Person
- (NSString)fangfaming1
{
NSString *str = NSStringFromSelector(_cmd);
return str;
}
+ (NSString)fangfaming2
{
NSString *str = NSStringFromSelector(_cmd);
return str;
}
@end
int main()
{
Person *p = [Person new];
NSString *s = [p fangfaming1];
NSString *s2 = [Person fangfaming2];
NSLog(@"%@ %@", s, s2);
}