一、分类-Category
1.基本用途:
如何在不改变原来模型的前提下,给类扩充一些方法有2种方式:
继承;
分类(category);
- 分类只能增加方法,不能增加成员变量
- 分类方法实现中可以访问原来类中声明的成员变量
- 分类可以重新实现原来类中的方法,但是会覆盖掉原来的方法,会导致原来的方法没法再使用
- 方法调用的优先级:分类(最后参与编译的分类优先) --> 原来类 --> 父类
2.格式:
分类的声明:
@interface 类名 (分类名称)
// 方法声明;
@end
分类的实现:
@implementation 类名 (分类名称)
// 方法实现;
@end
@interface Person (MJ) - (void)study; @end @implementation Person (MJ) - (void)study { NSLog(@"学习-----%d", _age); } - (void)test { NSLog(@"Person (MJ)-test"); } @end
3.好处:
一个庞大的类可以分模块开发;
一个庞大的类可以由多个人来编写,更有利于团队合作。
4.给系统自带的类添加分类,是分类的简单应用;
5.注意:
Category方法实现可以访问原始类的实例变量,但不能添加变量,只能添加方法。如果想添加变量,可以考虑通过继承创建子类;
Category可以实现原始类的方法(重新实现),但不推荐这么做,因为它是直接替换掉原来的方法,这么做的后果是再也不能访问原来的方法,分类优先级最高;
多个Category中如果实现了相同的方法,只有最后一个参与编译的才会有效,最后编译的会覆盖以前编译的;
二、类的本质
1.类也是个对象,类对象==类:就好像汽车是被实例后的对象,类是汽车的设计图,按照万物皆对象原则,这个设计图也是对象,也可以进行对象操作。而这类“设计图”对象有个统称:Class。简称“类对象”;
Class类型的定义:
typedef struct objc _class *Class;
int main() { Person *p =[[Person alloc] init]; // 内存中的类对象 // 类对象 == 类 Class c = [p class]; [c test]; Class c2 = [p2 class]; // 获取内存中的类对象 Class c3 = [Person class]; NSLog(@"c=%p, c2=%p, c3=%p", c, c2, c3); // 类本身也是一个对象,输出的类地址是相同的 return 0; }
类名就代表着类对象,每个类只有一个类对象。
2.获取类对象的2中方式:
类方法,某个类的class方法;
Class c = [Person class];
对象方法,某个对象的class方法;
Person *p = [Person new];
Class c2 = [p class];
都可以获取类对象,某个类通过上面两种方式获取类对象相同。
3.类对象调用类方法:
Class c = [Person Class];
Person *p2 = [c new];
4.+load和+initialize:在对象初始化、第一次使用时起到监听作用。
+load:
在程序启动的时候,就会加载项目中所有的类和分类,而且加载后会调用所有类和分类的+load方法;
先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load;
先加载原始类,再加载分类;
不管程序运行过程中有没有用到这个类,都会调用+load加载。
+initialize(当作类对象被使用时初始化):
在第一次使用某个类时(比如创建对象等),就会调用一次+initialize方法;
一个类只会调用一次+initialize方法,先调用父类的,再调用子类的。
//在Person.m中重写load和initialize方法
@implementation Person // 当程序启动的时候,就会加载一次项目中所有的类。类加载完毕后就会调用+load方法 + (void)load { NSLog(@"Person---load"); } // 当第一次使用这个类的时候,就会调用一次+initialize方法 + (void)initialize { NSLog(@"Person-initialize"); } @end //在Student.m中重写load和initialize方法 @implementation Student // 在类被加载的时候调用 + (void)load { NSLog(@"Student---load"); } + (void)initialize { NSLog(@"Student-initialize"); } @end //在Person+MJ..m中重写load和initialize方法 @implementation Person (MJ) + (void)load { NSLog(@"Person(MJ)---load"); } + (void)initialize { NSLog(@"Person(MJ)-initialize"); } @end int main() { Student * s = [[Studentalloc] init]; return 0; }
运行结果:
Person---load
Student---load
Person-initialize
Student-initialize
总结:
当程序启动时,就会加载项目中所有的类和分类,而且加载后会调用每个类和分类的+load方法,只会调用一次,分类的+load后调用;
当第一次使用某个类时,就会调用当前类的+initialize方法;
先加载分类,再加载子类(先调用父类的+load方法,再调用子类的+load方法),先初始化父类,再初始化子类(先调用父类的+initialize方法,再调用子类的+initialize方法)。
三、description方法(NSObject自带的方法)
1.-description方法(决定了实例对象的输出结果):
使用NSLog和%@输出某个对象时,会调用对象的-description方法;
拿到-description方法的返回值(NSString *)显示到屏幕上;
-description方法默认返回的是“类名+内存地址”。
2.+description方法(决定了类对象的输出结果):
使用NSLog和%@输出某个类对象时,会调用类的+description方法;
拿到+description方法的返回值(NSString *)显示到屏幕上;
+description方法默认返回的是“类名”。
3.修改NSLog的默认输出:
@implementation Person - (NSString *)description { // 下面代码会引发死循环 // NSLog(@"%@", self); return [NSString stringWithFormat:@"age=%d, name=%@", _age,_name]; }@end int main() { Person* p = [person new]; NSLog(@”%@”,p); //使用NSLog和%@输出某个对象时,会调用对象的-description方法 }
重写-description或者+description方法即可。
4.死循环陷阱:
如果在-description方法中使用NSLog打印self。
四、SEL
1.介绍:
SEL其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法;
把方法包装成SEL类型的数据;
根据SEL数据找到对应的方法地址;
根据方法地址调用对应的方法。
2.方法的存储位置:
每个类的方法列表都存储再类对象中;
每个方法都有一个与之对应的SEL类型的对象(数据);
根据一个SEL对象就可以找到方法的地址,进而调用方法;
SEL类型的定义:typedef struct objc _selector *SEL;。
3.SEL对象的创建:
SEL s = @selector(test);
SEL s2 = NSSelectorFromString(@"test");
4.SEL对象的其他用法:
将SEL对象转为NSString对象:
NSString *str =NSStringFromSelector(@selector(test));
调用对象p的test方法:
Person *p = [Person new];
[p performSelector:@selector(test)];
每个对象方法内部都内置了一个_cmd,_cmd代表着当前方法。
五、NSLog输出增强
1.打印某个对象的地址:
NSLog(@"%p", 对象);
NSLog(@"%@", 对象); // 默认情况,未重写-description
2.NSLog输出C语言字符串的时候,不能有中文;
3.__FILE__:源代码文件名;
4.__LINE__:NSLog代码再第几行;
5._cmd:代表着当前方法的SEL;
6.__func__:输出当前函数名。
黑马程序员---OC特有语法(二)
最新推荐文章于 2015-09-17 15:52:22 发布