结合之前的学习笔记以及参考《Objective-C编程全解(第三版)》,对Objective-C知识点进行梳理总结。知识点一直在变,只是作为参考,以苹果官方文档为准~
五、类对象
1、类的前置声明:@class
出现下列情况(循环引用)时:
Person.h中,包含Book对象
#import "Book.h"
@interface Person : NSObject
{
Book *_book;
}
@end
Book.h中,引用Person对象
#import "Person.h"
@interface Book : NSObject
{
Person* _author;
}
@end
此时,编译器会报错,unknown type name ‘Book’/’Person’,这是使用@class对类作一个声明就可以,说明是一个类,_book是Book类对象,_author是Person类的对象。
@class与#import区别
(1)@class只是告诉编译器Book是一个类名,但是这个类具体有什么样的实例变量和方法无法确定。(因此如果要在源文件.m中调用方法或引用实例变量时需要在.m中import)
(2)#import是一个预处理指令,把包含的文件的全部内容插入到当前文件夹中。
@class的优点
(1)可以避免循环引用引起的嵌套包含
(2)如果一个工程中多个文件#import了同一个头文件,当头文件要改动,所有包含此文件的都需要重新进行编译,编译效率较低,而@class不需要重新编译(不是嵌套包含,为了提高编译效率也可以用)
之前#import和#include的区别已经在其他篇章中提过,为了方便,这里再次罗列~
#import与#include
(1)两者都在预处理阶段把头文件的内容插入到当前的源文件中;
(2)#include多次导入同一文件,会导致重复包含,而#import会自动判断此前文件是否被导入过,若导入则不做导入操作,确保只导入一次。
2、id类型
(1)定义
typedef struct objc_object {
Class isa;
}*id;
OC中特有的数据类型,通用型(万能)指针类型,可以指向任意的OC对象;
(2)id类型可以指向任意类型的实例对象,到程序的运行期,会根据实际指向对象里面的isa指针找到所属的类,再去类中查找方法的入口地址,实现多态;
(3)id类型的变量不能使用点语法;
(4)注意:id类型一般用于方法的参数类型和返回值类型,不可滥用,能确定实例对象类型的要指定其所属的类
3、构造方法:类中用来初始化对象的成员变量的对象方法
(1)Person *p = [[Person alloc] init];
相当于
Person *p = [Person new];
alloc给此对象分配内存,类方法;
init初始化成员变量,NSObject中初始化为0,初始化失败返回nil
(2)子类重写init方法,使初始化值不为0
——>规则
a、必须调用父类init方法,初始化从父类继承过来的成员变量
b、验证父类成员是否初始化成功后,才能初始化子类新增的成员变量
c、返回经过了初始化后的当前实例对象self
- (instancetype)init {
self = [super init];//调用父类,有益于程序的扩展性,当父类成员发生变化时,只用在父类中修改,子类中不需要改动
if (self != nil) { //等价于if(self = [super init])
_age = 10;
}
return self;
}
(3)自定义构造方法
——>规则:
a、必须是对象方法
b、返回值必须是id类型
c、方法名以init开头,传参数后接With
- (id)initWithAge:(NSInteger)age andName:(NSString *)name {
if (self = [super init]) {
_age = age;
}
return self;
}
4、类对象的类型(本质)
(1)类就是一个对象,类型为Class,简称类对象(!!!Class跟id一样是指针类型,只是一个地址,并不需要了解实际指向的内容)
(2)类名就是类对象,可直接用类对象调用类方法
(3)获取类对象的方法:
a、类方法获取
Class c = [Person class];
b、对象方法获取
Person *p = [[Person alloc] init];
Class c1 = [p1 class];
NSLog(@"%p,%p",c,c1);地址的结果是一样的
5、类的加载和初始化过程
+ load:类对象加载后调用的方法
当程序开始运行的时候,会加载当前项目中所有的类,加载完成后自动调用 +load方法,只调用一次。
+initialize:类对象初始化的方法
当程序第一次使用某个类时,会调用此方法,只调用一次
类对象加载和初始化规则:
(1)先加载父类,再加载子类(初始化也相同)
(2)都可以重写,来验证先后顺序。
6、description方法
作用:直接输出实例变量,把对象的所有成员的值一一输出(打印的时候会自动调用)
a、对象方法:- description,以%@输出一个实例对象的结果
(1)返回值类型是NSString*,把这个字符串输出到屏幕上
(2)NSObject类的此方法默认返回 <类名:实例对象的内存地址>
(3)可重写,按照一定的格式输出实例变量,方便用户查看
- (NSString *)description {
return [NSString stringWithFormat:@"age=%li,name=%@",_age,_name];
}
b、类方法:+ description,以%@输出一个实例对象的结果
(1)返回值类型是NSString*,返回值为类名!
Class c1 = [Person class]; //类方法获取类对象
NSLog(@"%@", c1); //打印出来是Person
或者用对象方法获取类对象
(2)可重写类方法
7、几个对于Logging有用的宏
Macro | Format Specifier | Description |
---|---|---|
__func__ |
%s | Current function signature. |
__LINE__ |
%d | Current line number in the source code line. |
__FILE__ |
%s | Full path to the source code file. |
__PRETTY_FUNCTION__ |
%s | Like fun,but includes verbose type information in C++ code. |