1. 类方法和对象方法
对象方法
- 减号 – 开头
- 只能由对象来调用
- 对象方法中能访问当前对象的成员变量(实例变量)
类方法
- 加号 + 开头
- 只能由类(名)来调用
- 类方法中不能访问成员变量(实例变量)
类方法的好处和使用场合
- 不依赖于对象,执行效率高
- 能用类方法,尽量用类方法
- 场合:当方法内部不需要使用到成员变量时,就可以改为类方法
可以允许类方法和对象方法同名
2. self 关键字
self 的用途
- 谁调用了当前方法,self就代表谁
- self出现在 对象/类 方法中,self就代表 对象/类
- 成员变量和局部变量重名
当成员变量和局部变量同名时,采取就近原则,访问的是局部变量
用self访问成员变量,区分同名的局部变量
使用细节
- 出现的地方:所有的OC方法中(对象方法\类方法),不能出现在函数
- 作用
使用 “self->成员变量名” 访问当前 对象\类 方法调用的成员变量
使用 “[self 方法名];” 来调用方法(对象方法\类方法)
3. 继承
好处:
- 抽取重复代码
- 建立类之间的关系
- 子类可以拥有父类中 所有的 成员变量 和 方法
坏处:增强了代码的耦合性
重写:子类 重新实现 父类的某个方法,覆盖父类以前的方法
使用情境:
- A是B,则A继承B
- A拥有B,则A组合B「即B为A内部的一个成员变量」
super 关键字
- 直接调用父类的 成员变量 和 对象方法
- 使用情景:子类重写父类方法时想保留父类的一些行为
注意
- Objective-C 是 单继承
- 基本上所有类的根类是NSObject
- 不允许子类和父类拥有相同名称的成员变量
- 父类必须声明在子类前面「实现不用在子类前」
- 调用某个对象/类的方法时,优先去当前 对象/类 中找,若找不到去父类中找
- 子类会继承父类所有的变量「不管是否是私有,只是能不能直接访问的问题」
- 父类只在 implementation 里定义的变量,子类也会继承
4. 多态「多种形态」
定义:父类指针 指向 子类对象
作用:若参数中使用的父类类型,则可以传入父类、子类对象
注意
- 编译时 编译器会检查当前类型对应的类中有没有调用方法「因为OC是弱语法」
- 运行时 系统会动态检测对象的真实类型
- 局限性:父类型变量不能直接调用子类的方法「可以通过强制转换来实现」
5. 对象和对象的关系
组合关系:类型相同 或 实现同一接口 的对象组合到一起
依赖关系:A对象 做 B对象的 形参 或 方法的局部变量,B 依赖 A
关联关系:A对象 做 B对象的 一个成员变量,A 和 B 有关联关系
6. 构造方法
完整的创建一个对象「new 方法的步骤」
- 调用 +alloc 方法,分配存储空间,返回一个对象 Cat *c = [Cat alloc];
- 调用 -init 方法,进行初始化,把成员变量的值初始化为 0,返回初始化的原来对象 c = [c init];
[Cat new] == [[Cat alloc] init]
目的:对象创建出来,成员变量就有一些固定的值
方法:
- 重写 – init 方法
// implementation 中
- (instancetype)init {
// 1.调用 父类「super」 的 init 方法:初始化父类中声明的 成员变量属性 和 其他属性
self = [super init]; // 得到当前对象 self
if (self != nil) { // 初始化成功,nil 是空 == 0
_min = 10; // 2. 进行子类内部成员的初始化
}
return self;// 返回已经初始化完毕的对象
}
- 自定义构造方法
// implementation 中
- (instancetype)initWithAge : (int)age {
// 简化写法
if (self = [super init]) {
_age = age;
}
return self;
}
7. 类的声明可以不写「仅仅警告」
- 有类的声明
// 声明
@interface Sample : NSObject {
// 可以有成员变量
int a; // 默认@protected}
- (void)test;
@end
// 实现
@implementation Sample {
// 也可以有成员变量「这种做法很少见」
int b; // 默认 @private
//注意:interface 和 implementation 不能声明同名的成员变量
}
- (void)test{
NSLog(@"有类的声明");
}
@end
- 没有类的声明「实现 必须写在main函数之前」
// 实现
@implementation Sample : NSObject
- (void)test{
NSLog(@"没有类的声明");
}
@end
8. id 和 instancetype 数据类型
实质:id 和 instancetype 都是 万能指针类型,能 指向/操作 任何 objc 对象
区别:
- id 在编译时,不能 判断对象的真实类型
可以 定义变量,作为返回值,作为形参 - id 不能用点语法,id可以调用任何对象的方法「这样不利于编译器检查错误」
- instancetype 编译时,判断对象的真实类型
只能用于返回值
注意:自定义构造方法 返回值 尽量使用 instancetype 而不是 id
用法:
id d = [Person new]; // d为指针,id 类似于 NSObject *
NSObject *o = [Person new];
9. @class 关键字
- 格式:@class 类名1, 类名2 「注:为了可读性,一般一个@class 对应一个类名」
- 仅仅告诉编译器 类名 是个类,防止两个类互相调用.h文件 编译器报错
- 开发中引用的 类规范:
- 在 .h 文件中用 @class 来声明
- 在 .m 文件中用 #import 来包含类所有的东西
- @class 优点:
多个头文件 #import 了同一个头文件 a时,若 a 稍有改动,则这多个头文件就都要重新编译,为了减轻编译器的负担 使用 @class
10. 类的本质
类对象:类本身也是个对象,是 Class 类型对象
Class 类型定义:<code>typedef struct objc_class *Class;</code>
为了调用类方法,获取类对象「对象的类」:<code>Class n = [对象名 class];</code> 或 <code>Class n = [类名 class];</code>
11. 分类 Category
作用:在不改变原来类内容的基础上,为类添加方法「继承也有这样的作用」
好处:一个庞大的类可以分模块开发,由多人编写,更有利于团队合作
用法:分类可以定义在 单独的 .m文件和 .h文件中,也可以定义在原来类中
- 一般情况下,都定义在单独文件中
- 定义在原来类中的分类,只要能看懂语法就好
格式:
- 在 原类名+分类名.h 文件中「文件名是规范」
#import "原类名.h"
@interface 类名 (分类名)
// 方法声明;
@end
- 在 原类名+分类名.m 文件中
#import "原类名+分类名.h"
@implementation 类名 (分类名)
// 方法实现
@end;
注意:
- 分类只能增加 方法,不能增加 成员变量
- 分类方法可以访问原来类的成员变量
- 分类可以覆盖原来类同名的方法,会使类中原来的方法失效
- 方法调用优先级:1.分类「最后参与编译的分类优先」→ 2.原来类 → 3.父类
12. Class Extension 类扩展
定义:匿名的 Category,写在 .m/.h 文件中
作用:为某个类扩充一些私有的 成员变量 和 方法 的声明
格式:
#import "A.h" // 在 .m 文件中
// 类扩展
@interface A(){
int _a; // 私有的成员变量 _a
}
- (void)sum(int) :a addWith :(int)b; // 是声明
@end
@implementation A {
int _b;
}
- (void)function{
// 实现
}
@end