1.copy(自定义对象)
2.类的本质【理解】—》Class类对象
3.选择器 SEL【重点】
一 copy
对某个对象发送copy消息的时候,我们的目的是希望能够拷贝出一份一模一样的对象
【Demo】-【1-copy的使用】
什么是深拷贝和浅拷贝?
深拷贝:真正的复制出一份原来的对象,产生了一个新的对象;
浅拷贝:只拷贝了对象的地址,并没有产生新的对象;(retain就属于浅拷贝)
copy和retain的区别:
1.retain和copy都会让对象的引用计数+1,所以都应该对应的必须有一个release或 autorelease;
2.retain是让原对象引用计数器+1,这里只拷贝了对象的地址,并没有产生新对象,认为是“浅 拷贝”;
3.copy拷贝出了一个新对象,让新对象的引用计数器的值为1,认为是“深拷贝”;
copy的运行机制
//希望能拷贝出一个新对象,跟per1所指向的对象的值一样;
//当一个对象指向copy方法的时候,系统内部会自动的调用copyWithZone:这个方法,这是 一个协议方法,该方法返回的对象就是拷贝出来的新对象;
//以后凡是遇到copy一个自定义的对象,或者遇到属性使用copy修饰的自定义对 象,那么先对该对象遵守一个NSCopying协议,并实现协议方法;
#pragma mark - NSCopying协议方法
-(id)copyWithZone:(NSZone *)zone{
Person *per = [[Person alloc] initWithAge:self.age andWithName:self.name];
//在这里属于特殊情况,返回对象的时候不需要加入autorelease
return per;
}
二 类的本质
当程序一运行起来,系统就会默认吧工程中所有的类都加载一份到内存中(这块内存中保存的是 所有的方法列表),我们可以把这块内存认为是类在内存中的对象,称为“类对象”;
类对象都对应一种类型,Class类型;
Class-》所有的类对象都是Class类型;
类的加载和初始化【了解】
+(void)load;
+(void)initialize;
见【Demo】-【2-类的加载和初始化】
@implementation Dog
//当程序启动的时候,就会加载一次项目中所有的类到内存中,加载完毕后就会调用load方法(只会调用一次),先调用父类,再调用子类;
+(void)load{
NSLog(@"Dog ----load 加载到了内存中");
}
//当第一次使用这个类(类对象)的时候,就会调用initialize这个方法,且只调用一次,先调用父类,再调用子类;
+(void)initialize{
NSLog(@"Dog --- initialize 进行初始化!");
}
@end
类对象:一个类在程序中只有一个类对象;
类的对象:程序中可以有无数个类的对象;
如何获得Class类型的类对象;
1)直接向类的对象发送一个class消息,[per1 class];
2)直接向我们的类名发送class消息,[Person class];
见【Demo】-【3-类对象的使用场合】
//类对象的使用场合【**掌握下面的几个方法**】
//1.判断一个类是否是另一个类的子类
BOOL bl1 = [Person isSubclassOfClass:[NSObject class]];
if (bl1) {
NSLog(@"是的");
}else{
NSLog(@"不是的");
}
//2.判断一个指针所指向的对象是否是某种类型
id obj = [[Person alloc] init];
BOOL bl2 = [obj isMemberOfClass:[Person class]];
if (bl2) {
NSLog(@"是的");
}else{
NSLog(@"不是的");
}
//3.判断一个指针所指向的对象是否是某种类型或者它的子类【**常用**】
id str = @"1111222";
BOOL bl3 = [str isKindOfClass:[NSString class]];
if (bl3) {
NSLog(@"是的");
}else{
NSLog(@"不是的");
}
三 选择器【重点】
3.1认识选择器
1)选择器是一种数据类型,这个类型写作SEL
2)SEL声明的变量是用来装“成员方法”的,更确切的说,装“消息”,有点类似于“函数 指针”;
【作用】
我们可以通过选择器SEL数据类型实现动态的调用对象的成员方法。
【Demo】-【4-认识选择器】
数组的排序
见【Demo】-【5-数组的排序】
// char buf[100];
// printf("请输入您想调用的方法名:\n");
// scanf("%s",buf);
//
// //把C字符串转换成OC字符串
// NSString *str = [NSString stringWithUTF8String:buf];
//把一个字符串反射出对应的选择器类型
// SEL sel = NSSelectorFromString(str); //***********重点
Person *per = [[Person alloc] init];
//定义一个SEL类型的变量;
//该变量里装的是study这个成员方法;
// SEL sel = @selector(study);
//
//
// //通过选择器调用成员方法;
// [per performSelector:sel];
//
//
//
// SEL sel1 = @selector(setAge:);
// //调用带一个参数的方法;
// [per performSelector:sel1 withObject:@10];
// NSLog(@"per = %d",[per.age intValue]);
//
//
//
// [per performSelector:@selector(setName:) withObject:@"小迪迪"];
// NSLog(@"name = %@",per.name);
//调用带两个参数的 ,最多只能带两个参数;
//如果有两个以上的参数,可以考虑重新设计这个方法,比如传数组,或者设计另一个类把这些多个参数都封装在类里面,一起作为参数传过去;
// [per performSelector:<#(SEL)#> withObject:<#(id)#> withObject:<#(id)#>];
//选择器常用的几个方法
//1.判断一个“类”是否存在某个方法
Person *per2 = [[Person alloc] init];
BOOL bl1 = [Person instancesRespondToSelector:@selector(study)];
if (bl1) {
NSLog(@"存在该方法");
[per2 performSelector:@selector(study)];
}else{
NSLog(@"不存在该方法");
}
//2. 判断一个对象是否实现了某个方法 【***常用】
//常用在调用协议方法的时候,为了保证代码的安全性,在调用代理的协议方法之前,最好是先判断该代理对象是否实现了对应的协议方法;
BOOL bl2 = [per2 respondsToSelector:@selector(playGame)];
if (bl2) {
[per2 playGame];
}else{
NSLog(@"你没有实现这个方法!");
}
//3.协议选择器【了解】
//协议选择器里面装的是协议
Protocol *p = @protocol(NSCopying);
//判断一个对象是否遵守了某个协议
BOOL bl3 = [per2 conformsToProtocol:p];
if (bl3) {
if ([per2 respondsToSelector:@selector(copyWithZone:)]) {
Person *perCopy = [per2 copy];
[perCopy release];
}
}else{
NSLog(@"这个类不具备拷贝对象的能力");
}
数组排序
-(id)initWithAge:(int)age andWithName:(NSString *)name{
if ([super init]) {
self.age = age;
self.name = name;
}
return self;
}
-(NSComparisonResult)compareAndName:(Student *)otherStudent{
return [self.name compare:otherStudent.name];
}
-(NSComparisonResult)compareAndAge:(Student *)otherStudent{
if (self.age == otherStudent.age) {
return [self.name compare:otherStudent.name];
}
// return -[self.age compare:otherStudent.age];
//和上面写法一样的效果
return [@(self.age) compare:@(otherStudent.age)];
}