一、类的深入研究
类的本质:类本身也是一个对象,是个Class类型的对象,简称类对象。
(类名就是类对象,每个类只有一个类对象)。
Class类型的定义:
Typedef struct objc_class *Class;
Class 创建 Person类对象;
利用Person类对象,创建Person类型对象。
获取内存中的类对象:
Class c1 = [p class];
Class c2 = [p class];
Class c3 = [Person class];
NSLog(@"c1=%p,c2=%p,c3=%p",c1,c2,c3);
c1和c2还有c3地址相同。
总结:获取内存中的类对象有2种方法:
1.通过某个对象的class方法;
2.通过类的class方法。
二、类的加载和初始
+load和 +initialize
1.+load:
本类被加载的时候调用(类只加载一次);
当程序启动时,就会加载一次项目中的所有的类,类加载完毕后就会调用+load方法.
+ (void)load
{
NSLog(@"类名......load");
}
2.+initialize:
当第一次使用这个类时,就会调用一次initialize。
+ (void)initialize
{
NSLog(@"类名......initialize");
}
总结:
1.当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只调用一次;
2.先加载父类,再加载子类(先调用父类的+load方法,再调用子类的+load 方法);
3.先加载原来的类,再加载分类;
4.不管程序运行过程中有没有用到这个类,都会调用+load加载;
5.当第一次使用某个类时,就会调用当前的类的+initialize方法,
先初始化父类,再初始化子类,即先调用父类的+initialize方法,再调
用子类的+initialize方法;
6.若分类也有+initialize方法,只会调用分类的+initialize方法,不会
调用原来的类的+initia。
三、Description(类似于java toSring方法)
例子:默认情况下,利用NSLog 和 %@ 输出对象时,
结果是:<类名: 内存地址>
NSLog(@"%@",p);
分析过程:
1.会调用对象p的 - description方法;
2.拿到- description方法的返回值(NSString *)显示在屏幕上;
3.- description 方法默认返回的是: <类名 内存地址>。
修改 - description 输出对象内容(决定了实例对象的输出结果):
- (NSString *)description
{
Return
[NSString stringWithFormat:@"age = %d,name = %@",_age,_name];
}
此时,调用
NSLog(@"%@",p);
会输出例如这个结果:age = 15,name = "rose"
注意:一下这种使用会引发死循环
- (NSString *)description
{
NSLog(@"%@",self);
}
利用NSLog 和 %@ 输出类对象:
Class c = [Person class];
NSLog(@"%@",c);
输出结果:Person(类名)
分析过程:
1.会调用类的+description方法;
2.拿到+description方法返回值(NSSring *)显示在屏幕上。
例子:决定了类对象的输出结果:
+ (NSString *)description
{
// NSLog(@"%@",@"ABC");
return @"ABC"
}
注意:死循环陷阱
如果在 - description方法中使用NSLog打印self。
四、NSLog输出补充
指针变量地址:
NSLog(@"%p",&p);
对象的地址:
NSLog(@"%p",p);
<类名: 对象地址>:
NSLog(@"%@",p);
输出当前的函数名:
NSLog(@"%s\n",_func_);
输出行号:
NSLog(@"%d",_LINE_);
输出源文件的名称:
NSLog(@"%s",_FILE_);//NSLog输出c语言字符串时,不能有中文
五、SEL类型数据
SEL类型的定义:
typedef struct objc_selector *SEL;
例子:在内存中
SEL s1 == - test地址;
SEL s2 == - test2地址;
[p test2];
分析:
1.把test2包装成SEL类型的数据;
2.根据SEL数据找到相应的方法地址;
3.根据方法地址调用相应的方法;
间接调用test2方法:
[p performSelector:@selector(test2)];
例如: - (void)test3:(NSString *)abc
{
NSLog(@"调用了test3.....%@",abc);
}
也可这样调用test3
[p performSelector:@selector(test3:)withObject:@"456"];
方法的存储位置:
1.每个方法列表都存储在类对象中;
2.每个方法都有一个与之对应的SEL类型的对象;
3.根据一个SEL对象就可以找到方法的地址,进而调用调用方法。
SEL对象的创建:
SEL s = @selector(test3:);
调用test3方法:
[p performSelector:s withObject:@"456"];
把字符串对象转成SEL数据:
NSString *name = @"test2";
SEL s = NSSelectorFormString(name);
[p performSelector:s];
把SEL对象转NSString对象:
- (void)test
{
//_cmd代表着当前方法
NSString *str = NSStringFronSelector(_cmd);
NSLog(@"调用了方法test......%@",str);
}
[p test];
结果:调用了方法test.....test
注意:以下的代码会引发死循环
- (void)test
{
[self performSelector:self];
}
总结:SEL其实是对方法的一种包装,将方法包装成一个SEL数据,去找相应的方法地址,找到方法地址就可以调用方法。其实消息就是SEL。
六、练习
比较两个圆是否重叠:
/*
设计一个类Point2D用来表示二维平面中某个点
1.属性
* double x
* double y
2. 方法
* 属性相应的set方法和get方法
* 设计一个对象方法同时设置x和y
* 设计一个对象方法计算跟其他点的距离
* 设计一个类方法计算两个点之间的距离
3. 提示
* C语言中的math.h中有个函数:double pow(double n,double m);计算n的m次方
* C语言的math.h中有个函数:double sqrt(double n);计算根号n的值(对n进行开根)
4. 设计一个圆的类Circle
*/
i#import <Foundation/Foundation.h>
#import <math.h>
//点
@interface Point2D : NSobject
{
double _x; // x值
double _y; // y值
}
//设置x的setter和getter
-(void)setX:(double)x;
- (double)x;
//设置y的setter和getter
- (void)setY:(double)y;
- (double)y;
//同时设置x和y
- (void)setX:(double)x andY:(double)y;
//计算跟其他点的距离
- (double)distanceWithOther:(Point2D *)other;
//计算两个点之间的距离
+ (double)distanceBetweenPoint1:(Point2D *)p1 andPoint2:(Point *)p2;
@end
@implementation Point2D
-(void)setX:(double)x
{
_x = x;
}
- (double)x
{
return _x;
}
- (void)setY:(double)y
{
_y = y;
}
- (double)y
{
return _y;
}
//同时设置x和y
- (void)setX:(double)x andY:(double)y;
{
/*
_x = x;
_y = y;
*/
[self setX:x];
[self setY:y];
}
//计算跟其他点的距离
- (double)distanceWithOther:(Point2D *)other
{
//((x1-x2)的平方+(y1-y2)的平方)开根
double xDelta = [self x] - [other x];//_x - [other x];
double yDelta = [self y] - [other y];//_y - [other y];
double xDeltaPF = pow(xDelta, 2);
double yDeltaPF = pow(yDelta, 2);
return sqrt(xDelta + yDelta);
}
//计算两个点之间的距离
+ (double)distanceBetweenPoint1:(Point2D *)p1 andPoint2:(Point *)p2
{
return [p1 distanceWithOther:p2];
}
@end
@interface Circle : NSobject
{
double _radius; // 半径
Point *_point; // 圆心(组合)
}
//设置半径的setter和getter
- (void)setRadius:(double)radius;
- (double)radius;
//设置圆心的setter和getter
- (void)setPoint:(Point *)point;
- (void)point;
//判断跟其他圆是否重叠
//返回值是BOOL类型的,方法名一般是以is 开头
- (BOOL)isOverlappingWithOther:(Circle *)other;
+ (BOOL)isOverlappingBetweenCircle1:(Circle *)circle1 andCircle2:(Circle *)circle2;
@end
@implementation Circle
//设置半径的setter和getter
- (void)setRadius:(double)radius
{
_radius = radius;
}
- (double)radius
{
return _radius;
}
//设置圆心的setter和getter
- (void)setPoint:(Point *)point
{
_point = point;
}
- (Point *)point
{
return point;
}
//圆心之间的距离> 半径之和则不重叠返回NO
//圆心之间的距离< 半径之和则重叠 返回YES
- (BOOL)isOverlappingWithOther:(Circle *)other
{
Point *p1 = [self point];
Point *p2 = [other point];
double distance = [p1 distanceWithOther:p2;
double sumRadius = [self radius] - [other radius];
return distance < sumRadius;
}
+ (BOOL)isOverlappingBetweenCircle1:(Circle *)c1 andCircle2:(Circle *)c2
{
return [c1 isOverlappingWithOther:c2];
@end
int main()
{
/*
Point *p1 = [Point new];//创建一个新的对象p1
//设置x和y的值
[p1 setX:10 andY:15];
Point *p2 = [Point new];// 创建一个新的对象p2
//设置x和y的值
[p2 setX:13 andY:19];
//计算p1和p2之间的距离
double d = [p1 distanceWithOther:p2];
NSLog(@"%d",d);
//调用类方法,计算p1和p2之间的距离
double d2 = [Point distanceBetweenPoint1:p1 andPoint2:p2];
NSLog(@"%d",d2);
*/
//创建一个圆的对象
Circle *c1 = [Circle new];
// 设置圆的半径
[c setRadius:3];
//创建圆心的对象
Point *p1 = [Point new];
//设置x和y的值
[p1 setX:10 andY:15];
[c1 setPoint:p1];
//设置一个圆的对象c2
Circle *c2 = [Circle new];
//设置c2的半径
[c2 setRadius:5];
Point *p2 = [Point new];
[p2 setX:13 andY:19];
[c2 setPoint:p2];
BOOL b1 = [c1 isOverlappingWithOther:c2];
NSLog(@"%d"b1);
}
给NSString 分类写个类方法,输出一个字符串中数字的个数:
/*
给NSString添加一个类方法,计算字符串中数字的个数
*/
#import <Foundation/Foundation.h>
//#import <NSString+Number>,多文件开发时.m文件的头文件
@interface NSString(Number)
//方法的声明
+ (int)numberOfString:(NSString *)str;
@end
@implementation NSString(Number)
//方法的实现
+ (int)numberOfString:(NSString *)str
{
int count = 0;
for(int i = 0;i < str.length;i++)
{
//返回第i个位置的字符
unichar c= [str characterAtIndex:i];
//注意字符串是由字符构成的,单个字符要用''包住
if(c >= '0' && c <= '9')
{
count++;
}
}
return count;
}
@end
int main()
{
//NSString 类调用方法
int count = [NSString numberOfString:@"wo shi 123 wang"];
//输出数字的个数
NSLog(@"%d",count);
return 0;
}
运行结果:3
给NSString增加一个对象方法,计算当前对象里面的阿拉伯数字的个数:
/*
给NSString 添加一个对象方法,计算当前对象里面的阿拉伯数字的个数
*'/
#import <Foundation/Foundation.h>
@interface NSString
//方法的声明
- (int)numberCount;
@end
@implementation NSString
//方法的实现
- (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
int main()
{
NSString *str = @"woshi123wang";
int count = [str numberCount];
NSLog(@"%d",count);
return 0;
}
运行结果:3