————————Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ———————
一、类的本质
1.类对象的概念
类本质上也是对象,简称类对象。
Class类在框架中定义为:
typedef struct objc_class *Class;
类名就代表类对象,每一个类只有一个类对象。
我们类比一下:
利用Person类对象创建Person类型的对象:
Person *p = [[Person alloc] init];
利用Class类创建Person类对象:
Class c = [Person class];
这句的目的是获取内存中的对象。
2.类对象的使用
int main()
{
Person *p = [[Person alloc] init];
//[Person test];
// 内存中的类对象
// 类对象 == 类
Class c = [p class];
[c test];
Person *p2 = [[c new] init];
NSLog(@"00000");
}
二、类的加载与初始化(细节)
1.加载
在运行程序时,每一个类会自动先加载类+ (void)load方法,加载的顺序为:加载父类---->加载子类。
类的加载是在.m实现中系统自带的+ (void)load类方法。
即使main函数中没有调用加载,运行中也会自动先加载类+ (void)load方法。
那么我们怎么知道何时加载了这个类呢?(即如何监听类的加载?)
我们对类进行重写:
Person.h
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
+ (void)test;
@end
Person.m
#import "Person.h"
@implementation Person
+ (void)test
{
NSLog(@"调用了test方法");
}
// 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法
+ (void)load
{
NSLog(@"Person---load");
}
// 当第一次使用这个类的时候,就会调用一次+initialize方法
+ (void)initialize
{
NSLog(@"Person-initialize");
}
@end
Student.h
#import <Foundation/Foundation.h>
#import "Person.h"
@interface Student : Person
@end
Student.m
#import "Student.h"
@implementation Student
// 在类被加载的时候调用
+ (void)load
{
NSLog(@"Student---load");
}
+ (void)initialize
{
NSLog(@"Student-initialize");
}
@end
main.m
#import <Foundation/Foundation.h>
int main()
{
[[Student alloc] init];
return 0;
}
调用子类,是先加载初始化父类之后,才加载子类。
因此输出结果为
Person-load
Student-load
Person-initialize
Student-initialize
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每一个类和分类+load方法,只会调用一次。
2.当第一次使用某个类时,就会调用当前类的+initialize方法(先找分类中有没有,然后找该类,最后找父类)。
3.先加载父类,再加载子类(先调用父类+load方法,再调用子类+load方法)
先初始化父类,再初始化子类(先调用父类+initialize方法,再调用子类+initialize方法)
因此用+initialize可以用来监听类的第一次使用!
三、description方法
description方法时NSObject自带的方法,有对象方法-description和类方法+description
1.description方法的使用
大家有没留意到我们用NSLog函数的时候,能够使用%@操作符来输出对象的一些参数,这是怎么实现的呢?
原来在每个对象类中都存在一个description方法,用于输出对象的一些特性,默认情况下,我们自定义的对象类中的这个方法不输出任何内容,我们可以重写这个方法,以输出必要的对象信息,输出格式可以自己定义。如下:
@interface Student : Person
@property (nonatomic) int number;
@end
@implementation Student
+ (NSString *)description{
return [NSString stringWithFormat:@"%@\nnumber = %d\n", [[super description], number]; // 父类调用 // 输出number
}
@end
下面我们设计一个Person类的子类,在这个类里,重写了description方法:
Student *s=[Student new];
s.name = @"Xiaoming";
s.age = 18;
s.number = 10;
NSLog(@"%@", s); // 通过%@输出对象参数
结果输出为
2015-02-15 19:19:21.627 Study[2682:303] <Student: 0x10010a0c0>
number = 10
对比之下我们不难发现:
默认情况下,利用NSLog和%@输出对象时,结果是:<类名:内存地址>,在不重写description方法的情况下是这样输出的。
除非要输出NSString*类型的返回值,会正常输出。如:
NSString *name = @"Jack";
NSLog(@"我的名字叫%@", name);
输出结果是我们想要的
Jack
其余的若想输出我们想要的值就需要对-description进行重写。
那么如何重写?我们举一个例子。
(由于重写内容只需要修改.m文件,所以我们只将.m文件贴上来)
Person.m
#import "Person.h"
@implementation Person
//决定了实例对象的输出结果
- (NSString *)description
{
return [NSString stringWithFormat:@"age = %d, name = %@", _age, _name];
}
@end
这样就能得到我们想要的输出结果。
总结:
当我们需要输出%@的时候,那么我们可以利用description方法重写,得到我们想要的输出结果。
同样地,若想输出一个对象p,则修改-description对象方法;若想改变类的输出,则修改+description类方法。
四、NSLog输出补充
NSLog输出行号:
NSLog(@"%d", __LINE__);
NSLog输出C语言字符串的时候,不能有中文
NSLog(@"%s", __FILE__);
但是printf可以输出中文:
printf("%s\n", __FILE__);
NSLog输出当前函数名
NSLog(@"%s\n",__func__);
五、SEL方法
SEL其实就是消息,发送消息其实就是发送SEL
1.方法的存储位置
1)每个类的方法列表都存储在类对象中
2)每个方法都有一个与之相对应的SEL类型的对象
3)根据一个SEL对象就可以找到方法的地址,进而调用方法
SEL定义
typedef struct objc selector *SEL
2.SEL对象的创建
1)SEL s = @selector(test);
比如:
SEL s = @selector(test3:);
[p performSelector:s withObject:@"123"];
2)SEL s2 =NSSelectorFromString(@"test");
比如:
NSString *name = @"test2";
SEL s = NSStringFromString(name); //把一个字符串转成一个SEL类型的数据
[p performSelector:s];
3.SEL对象的其他用法
1)将SEL对象转为NSString对象
NSString *str = NSStringFromSelector(@"selector(test)");
Person *p = [Person new];
2)调用对象p的test方法
[p performSelector:@selector(test)];
其实每个方法内部都有_cmd数据室SEL方法
//_cmd代表着当前方法
//_cmd==@selector(test2);
- (void)test2
{
NSString *str = NSStringFromSelector(_cmd);
NSLog(@"调用了test2方法-%@", str);
}
这里输出test2.。