六、 分类
知识点
主题1:分类category依赖于类,新建时,创建category模板
1.不改变原来类模型的基础上来扩充方法
2.格式:
@interface 类名(分类名称)
-
@end
@implementation 类名(分类名称)
@end
3.调用:首先把相应的头文件包含进来
[p study];//和以前调用方式一模样
4.如果(庞大的)类方法很多(模块)时,要好多人去写,这时就可以按人去分类,然后调用的时候该咋调用咋调用
5.总结:
1)不改变原来类内容的基础上为类增加一些方法。
2)只能增加方法,不能增加成员变量
3)分类的实现是可以访问原来类里的成员变量
4)如果扩充的方法名和原来的方法名一样时(重写),会覆盖原来的方法。
因为分类的方法的调用优先级高于原来的类方法,就是先去分类中找,然后再去原来的类去找,最后去父类
5)那如果多个扩充文件里也存在的一样的呢?测试下,结果:是调用最后编译的分类方法。
(因为相同名字方法编译一次覆盖一次,只保留最后一个)
主题2:给NSString 类添加类方法
1.系统自带的类,有时不能满足自己的需要:比如:要求求出字符串中含有的阿拉伯数字的个数
2.步骤:
1*选在分类category模板 在NSString上分
2*扩充一个类方法 +开头
3.声明和实现文件
//.h
#import <Foundation/Foundation.h>
@interface NSString (Number)
+ (int)numberCountOfString:(NSString *)str;
- (int)numberCount;
@end
//.m
#import "NSString+Number.h"
@implementation NSString (Number)
+ (int)numberCountOfString:(NSString *)str
{
int count=0;//局部变量要进行初始化,不然后果很严重
for (int i=0;i<[str length];i++
{
unichar c=[str characterAtIndex:i];//获取这个字符串的第i个字符
if ( c>=‘0’&&c<=‘9’)
{
count++;
}
}
return count;
}
@end
主题3:给NSString 类添加对象方法
#import "NSString+Number.h"
@implementation NSString (Number)
- (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;
}
@end
技巧:可以在类方法里调用对象方法,也可以在对象方法里调用类的方法
类库:很多类的集合 包含它分类的头文件 就可以使用别人写好的东西了
七、 类的深入
知识点
1.类的本质
1)实际上类也是一个对象,那么这个对象的类型是什么?是Class类型,Class里已经包含* 里;简称类对象
2)利用Class 创建 Person类对象
3)利用Person类对象,创建 Person类型的 对象 Person *p=[Person new];
4)获取内存中的类对象:Class c=[p class]; //打印地址:%p;
5)获取内存中的类对象2:Class c=[Person class];//和4)结果是一样的
2.类对象的使用
1)调用类方法 + (void)test;
一般:利用类名 调用 [Person test];//类名就代表类对象
现在:Class c=[p class]; [c test];//用类对象调用
2)创建对象:Class c=[p class]; Person *p=[[c new] init];
3)一个类在内存中只拥有一份存储空间,这个空间就叫类对象
3.类的加载和初始化
1)类的加载:在加载时,调用+ (void)load;这个类方法。由系统自动调用(当程序用没用类时,都会做这件事)://程序一启动加载类时
2)利用这个特性,来监听
3)当程序使用类时,又会做更深调用:+(void)initialize;
4)测试,重写这些类方法打印下
5)分类category里,也会具有上述特性,优先选择分类的初始化,类的初始方法不再调用;但是加载时,类和分类都会加载
6)当程序启动时,就会加载项目中所有类和分类,而且加载后会调用每一个类和分类的+load方法,只会调用一次
7)当第一次使用某个类时,就会调用当前类的+initialize方法
8)先加载父类,再加载子类(先调用父类的+load再调用子类的+load)先加载原始类,再加载分类
当有分类时,分类优先调用+initialize,类不再调用+initialize)
9)想类第一次使用的时候,做些事情;这时就利用+initialize方法 //监听
代码举例
#import "Person.h"
@implementation Person
+ (void)test
{
NSLog(@"调用了test方法");
}
// 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
NSLog(@"Person---load");
}
// 当第一次使用这个类的时候,就会调用一次+initialize方法
+ (void)initialize
{
NSLog(@"Person-initialize");
}
@end
//对应的main函数 注意把相应的头文件导入
int main()
{
// 利用Person这个类创建了2个Person类型的对象
Person *p = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
Person *p3 = [[Person alloc] init];
// 获取内存中的类对象
Class c = [p class];
Class c2 = [p2 class];
// 获取内存中的类对象
Class c3 = [Person class];
NSLog(@"c=%p, c2=%p, c3=%p", c, c2, c3);
}
八、 description方法
知识点
description方法:在NSObject.h文件里可以找到
1.一种是-开头 类的
2.一种是+开头 对象的
3.需求:一个类的所有属性打印出来
方法1:NSLog(@“%@”,p);//打印OC对象,默认打印的时类名和对象地址;但是打印字符串,打印的却是字符串???;不能满足需求
原理:首先去调用对象p的-description方法,这个方法的返回值(NSString *),NSLog让这个返回值显示在屏幕上
-description方法默认返回的时 类名+内存地址
方法2:重写description方法来覆盖父类NSObject的description
-(NSString *)description()
{
return @“13333”;
}
//实现需求
-(NSString *)description()//由NSLog的p调用;决定实例对象的输出结果
{
return [NSString stringWithFomat(@“name=%d”,_name)];
}
4.+开头的description
需求:拿到类对象后,想输出类对象时,
打印时,会先调用类的+description方法,拿到这个的返回值,显示在屏幕上;默认返回值是类名;所以这个方法决定了类对象的输出结果
5.总之重写description可以更改输出样式,注意不能在这个方法里用NSLog,否则会造成死循环
九、 NSLog
知识点
1*指针变量的地址NSLog(@“%@”,&p)和p里存储的对象地址NSLog(@“%@”,p)不是一个概念
2*下划线下划线 __LINE__//输出行号;这个在文档帮助里的 Objctive-C→improved log。。。文件里可以找到
3*NSLog输出C语言字符串(char= “ddd就”)时,不能有中文,否则输不出任何东西
4*__func__:输出当前函数名
5*__FILE__:输出源文件名
6*_cmd :代表着当前方法的SEL
// 下面的代码会引发死循环
- (void)test {
[self performSelector:_cmd];
}
十、 SEL
一种数据类型:SEL:一个SEL数据就代表一个方法
知识点
1.方法本身是怎么存的???
首先分配存储空间给这个类,类里面有个方法列表,但是这些方法是怎么存的呢?
注意:每一个方法在内存中都有一个SEL的数据和它对应。
方法调用:利用对象的isa找到它的类,然后去找这个方法
2.程序首先会把这个test包装成SEL类型数据,通过这个数据去找到这个方法地址,
最终通过这个地址找到所要调用方法,显然这个SEL数据里有方法的地址
注意:有个缓存列表存储上次查询后的结果,所以先到缓存去找,找不到,再去内存去找
3.怎么创建SEL数据:SEL s=@selector(方法名:);//有参数的方法 的 方法名是由冒号:的;然后可以用%d 输出
4.通过SEL间接调用方法:[p perfomSelector:s] //可以传参的 WithObject(id)特别注意→_→:有参数的方法 的 方法名是由冒号:的
5.打印字符串:%@
6.SEL:@selector的前三个字母的大写,它就是对方法的一个包装,所以可以通过它也能找到方法
7.方法都存在类对象中的
9.每个方法内部都有一个SEL类型的变量:_cmd//它代表当前方法;要打印出来它的值需要先转换成字符串 NSStringFormSelector(_cmd);
10.在方法里写:[self performSelector:_cmd];//会自己调用自己,陷入死循环
11.SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址。找到方法地址就可以调用方法
12.调用的方法很频繁时,可以直接把方法地址搞过来,直接去找到这个方法。高效
13.说的发消息就是发一个SEL类型的数据,根据这个数据找到方法的地址找到方法。所以本质上消息就是SEL。
代码举例
#import <Foundation/Foundation.h>
#import "Person.h"
int main()
{
Person *p = [[Person alloc] init];
[p test2];
NSString *name = @"test2";
SEL s = NSSelectorFromString(name);//把test2包装成SEL类型的数据
[p performSelector:s];//根据SEL数据找到对应的方法地址
return 0;
}