1. property
- 基本使用 - - - 编译器只要看到@property, 就会自动生成某一个属性的getter/setter方法的声明
2. @synthesize
- @synthesize是一个编译器指令, 它可以简化我们getter/setter方法的实现
- 什么是实现: 在声明后面写上大括号就代表着实现
- 在@synthesize后面告诉编译器, 需要实现哪个@property生成的声明
- 告诉@synthesize, 需要将传入的值赋值给谁和返回谁的值给调用者
- 如果在@synthesize后面没有告诉系统将传入的值赋值给谁, 系统默认会赋值给和@synthesize后面写得名称相同的成员变量
3. property的增强:
- 默认@property会将传入的属性赋值给_开头的成员变量
- @property有一个弊端: 它只会生成最简单的getter/setter方法的声明和实现, 并不会对传入的数据进行过滤
- 如果想对传入的数据进行过滤, 那么我们就必须重写getter/setter方法
- 如果重写了setter方法, 那么property就只会生成getter方法
- 如果重写了getter方法, 那么property就只会生成setter方法
- 如果同时重写了getter/setter方法, 那么property就不会自动帮我们生成私有的成员变量
- 如果不想对传入的数据进行过滤, 仅仅是提供一个方法给外界操作成员变量, 那么就可以使用@property
- 如果利用@property来生成getter/setter方法, 那么我们可以不写成员变量, 系统会自动给我们生成一个_开头的成员变量
- 注意: @property自动帮我们生成的成员变量是一个私有的成员变量, 也就是说是在.m文件中生成的, 而不是在.h文件中生成的
- property的修饰符
- 如果给一个属性同时提供了getter/setter方法, 那么我们称这个属性为可读可写属性(readwrite — 默认就是这个)
- 如果只提供了getter方法, 那么我们称这个属性为只读属性(readonly)
- 如果只提供了setter方法, 那么我们称这个属性为只写属性(readonly)
- 如果既没有提供getter也没有提供setter方法, 那么我们称这个属性为私有属性
- 程序员之间有一个约定, 一般情况下获取BOOL类型的属性的值, 我们都会将获取的方法名称改为isXXX
- @property(getter=isMarried) BOOL married; // 是否已婚
- 这么写, married的getter方法名, 就是isMarried
- 同理, 也可以用setter=方法名, 把setter的方法名也重写了
5. id (动态数据类型)
- id是一个数据类型, 并且是一个动态数据类型
- 默认情况下所有的数据类型都是静态数据类型
- 静态数据类型的特点:
- 在编译时就知道变量的类型,
- 知道变量中有哪些属性和方法
- 在编译的时候就可以访问这些属性和方法,
- 并且如果是通过静态数据类型定义变量, 如果访问了不属于静态数据类型的属性和方法, 那么编译器就会报错
- 动态数据类型的特点:
- 在编译的时候编译器并不知道变量的真实类型, 只有在运行的时候才知道它的真实类型
- 并且如果通过动态数据类型定义变量, 如果访问了不属于动态数据类型的属性和方法, 编译器不会报错
- id == NSObject * 万能指针
- id和NSObject *的区别:
- NSObject *是一个静态数据类型
- id 是一个动态数据类型
- 通过静态数据类型定义变量, 不能调用子类特有的方法
- 通过动态数据类型定义变量, 可以调用子类特有的方法
- 通过动态数据类型定义的变量, 可以调用私有方法
- 弊端: 由于动态数据类型可以调用任意方法, 所以有可能调用到不属于自己的方法, 而编译时又不会报错, 所以可能导致运行时的错误
- 应用场景: 多态, 可以减少代码量, 避免调用子类特有的方法需要强制类型转换
- 为了避免动态数据类型引发的运行时的错误, 一般情况下如果使用动态数据类型定义一个变量, 在调用这个对象的方法之前会进行一次判断, 判断当前对象是否能够调用这个方法
id obj = [Student new];
/*
if ([obj isKindOfClass:[Student class]]) {
[obj eat];
}
*/
if ([obj isMemberOfClass:[Student class]]) {
[obj eat];
}
6. new方法实现原理
- new做了三件事情
- 开辟存储空间 + alloc 方法
- 初始化所有的属性(成员变量) - init 方法
- 返回对象的地址
- 示例
Person *p1 = [Person alloc];
Person *p2 = [p1 init];
NSLog(@"p1 = %p, p2 = %p", p1, p2);
7. 构造方法基本概念
- 在OC中init开头的方法, 我们称之为构造方法
- 构造方法的用途: 用于初始化一个对象, 让某个对象一创建出来就拥有某些属性和值
- 重写init方法, 在init方法中初始化成员变量
- 注意: 重写init方法必须按照苹果规定的格式重写, 如果不按照规定会引发一些未知的错误
- 必须先初始化父类, 再初始化子类
- 必须判断父类是否初始化成功, 只有父类初始化成功才能继续初始化子类
- 返回当前对象的地址
- 一定要将[super init]的返回值赋值给self
8. instancetype和id的区别
instancetype == id == 万能指针 == 指向一个对象
- id在编译的时候不能判断对象的真实类型
- instancetype在编译的时候可以判断对象的真实类型
id和instancetype除了一个在编译时不知道真实类型, 一个在编译时知道真实类型以外, 还有一个区别
- id可以用来定义变量, 可以作为返回值, 可以作为形参
- instancetype只能用于作为返回值
注意: 以后但凡自定义构造方法, 返回值尽量使用instancetype, 不要使用id
9. 自定义构造方法
- 自定义构造方法:
- 其实就是自定义一个init方法
- 一定是对象方法
- 一定返回id/instancetype
- 方法名称一定以init开头
- (instancetype)initWithAge:(int)age;
- 一个类可以有0个或者多个自定义构造方法
- 自定义构造方法可以有1个或多个参数
- (instancetype)initWithAge:(int)age andName:(NSString *)name;
- 自定义构造方法在继承中的表现
- 注意: 自己的事情自己做 ( 属性是在哪个类里定义的, 就应该由这个类始终负责赋值 )
- 自定义构造方法init的顺序: 子类init => 父类init => NSObject的 init => NSObject的return => 父类的return => 子类的return
- 注意: 属性名, 不要以new开头, 有可能引发未知错误 ; 方法名也不要以new开头
11. 类工厂方法的基本概念
- 什么是类工厂方法:
- 用于快速创建对象的类方法, 我们称之为类工厂方法
- 类工厂方法中主要用于 给对象分配存储空间和初始化这块存储空间
- 规范:
- 一定是类方法 +
- 方法名称以类的名称开头, 首字母小写
- 一定有返回值, 返回值是id/instancetype
- 自定义类工厂方法是苹果的一个规范, 一般情况下, 我们会给一个类提供自定义构造方法和自定义类工厂方法用于创建一个对象
- 类工厂方法在继承中的注意点
- 注意: 但凡自定义类工厂方法, 在类工厂方法中创建对象, 一定要用self来创建
- self在类方法就代表类对象, 到底代表哪个类对象?? 谁调用当前方法, self就代表谁
13. 类的本质
- 所有类的”类对象”的继承关系就是”元类对象”的继承关系
- 每个对象都有isa,
- 实例对象isa => 类对象
- 类对象isa => 元类对象
- 元类对象isa => 根元类对象
- 根元类对象isa => 根元类对象(指向它自己) 根元类对象, 就是NSObject
14.类对象的获取和使用场景
- 如何获取类对象
- 语法: Class test = [类名 class];
- 注意: 一个类在内存中只有一分类对象
- 类对象的应用场景
15.类的启动过程
- 只要程序启动, 就会将类的代码加载到内存中. 放到代码区
- load方法会在当前类被加载到内存的时候调用, 有且仅会调用一次
- 如果存在继承关系, 会先调用父类的load方法, 再调用子类的load方法
+ (void) load {}
- initialize方法在当前类第一次被使用的时候就会调用(创建类对象的时候)
- initialize方法在整个程序的运行过程中只会被调用一次, 无论你使用多少次这个类都只会调用一次
- initialize用于对某一个类进行一次性的初始化
+ (void) initialize { }
16. SEL类型
- SEL类型的第一个作用, 配合对象/类来检查对象/类中有没有实现某一个方法
- respondToSelector 判断某个对象有没有实现规定的方法 (会根据调用对象自动识别对象方法和类方法)
SEL sel = @selector(setAge:);
Person *p = [Person new];
BOOL flag = [p respondsToSelector:sell];
NSLog(@"flag = %i",flag);
SEL sel1 = @selector(test);
flag = [p respondsToSelector:sel1];
NSLog(@"flag = %i",flag);
flag = [Person respondsToSelector:sel1];
NSLog(@"flag = %i",flag);
- SEL类型的第二个作用, 配合对象/类来调用某一个SEL方法
- performSelector 只能传对象, 可以传0 ~ 2个参数
SEL sel = @selector(demo);
Person *p = [Person new];
[p performSelector:sel1];
SEL sel1 = @selector(signalWithNumber:);
[p performSelector:sel1 withObject:@"13900000001"];
SEL sel2 = @selector(setAge:);
[p performSelector:sel2 withObject:@(5)];
NSLog(@"age = %i",p.age);
SEL sel3 = @selector(sendMessageWithNumber:andContent:);
[p performSelector:sel3 withObject:@"13900000001" withObject:@"abcdef"];
Car *c = [Car new];
SEL sel = @selector(run);
Person *p = [Person new];
[p makeObject:c andSel:sel];