1. 自动释放池
1. 自动释放池的原理
存入到自动释放池中的对象, 在自动释放池被销毁的时候, 会自动调用存储在该自动释放池中的所有对象的release方法.
可以解决的问题:
将创建的对象, 存入到自动释放池中, 就不再需要手动的release这个对象了
因为池子销毁的时候, 就会自动的调用池中所有的对象的release
自动释放池的好处:将创建的对象存储到自动释放池中, 不需要再写release了
2. 如何创建自动释放池
@autoreleasepool{
}
这对大括号代表这个自动释放池的范围
3. 如何将对象存储到自动释放池之中
在自动释放池之中调用对象的autorelease方法, 就会将这个对象存入到当前自动释放池之中, 这个autorealease方法返回的是对象本身, 所以, 我们可以这么写
@autoreleasepool
{
Person *p1 = [[[Person alloc]init]autorelease];
}
这个时候, 当这个自动释放池执行完毕之后, 就会立即为这个自动释放池中的对象发送1条release消息
目前为止, 我们感受到了autorelease的好处
创建对象, 调用对象的autorelease方法, 将这个对象存入到当前的自动释放池中,我们就不需要再去release, 因为自动释放池销毁的时候, 就会自动的调用池中所有对象的release
4. 使用注意
1).只有在自动释放池中调用了对象的autorelease方法, 这个对象才会被存储到这个自动释放池之中,
如果只是将对象的创建代码写在自动释放之中, 而没有调用对象的autorelease方法,是不会将这个对象存储到这个自动释放池之中的.
2). 对象的创建可以在自动释放池的外面, 在自动释放池之中, 调用对象的autorelease方法, 就可以将这个对象存储到这个自动释放池之中.
3). 当自动释放池结束的时候, 仅仅是对存储在自动释放池中的对象发送1条release消息, 而不是销毁对象.
4). 如果在自动释放池中, 调用同1个对象的autorelease方法多次,就会将对象存储多次到自动释放池之中,在自动释放池结束的时候, 会为对象发送多条release消息, 那么这个就可能出现僵尸对象错误,所以, 1个自动释放池之中,只autorelease1次, 只将这个对象释放1次,否则就会出现野指针错误
5).如果在自动释放池中, 调用了存储到自动释放池中的对象的release方法,在自动释放池结束的时候,还会再调用对象的release方法, 这个时候就有可能会造成野指针操作
也可以调用存储在自动释放池中的对象的retain方法
6).将对象存储到自动释放池, 并不会使对象的引用计数器+1
所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了
7).自动释放池可以嵌套
调用对象的autorelease方法,会将对象加入到当前自动释放池之中
只有在当前自动释放池结束的时候才会将对象发送release消息
5. autorelease的规范
0). 创建对象, 将对象存储到自动释放池之中,就不需要再去手动的release
1).类方法的第1个规范:
一般情况下, 要求提供与自定义构造方法相同功能的类方法,这样可以快速的创建1个对象
2).我们一般情况下,写1个类,会为我们的类写1个同名的类方法,用来让外界调用类方法来快速的得到1个对象
规范: 使用类方法创建的对象,要求这个对象在方法中就已经被autorelease过了,
这样,我们只要在自动释放池中,调用类方法来创建对象, 那么创建的对象就会被自动的加入到自动释放池中
提供1个类方法来快速的得到1个对象
规范:
a.这个类方法以类名开头, 如果没有参数就直接是类名, 如果有参数就是 类名WithXX:
b.使用类方法得到的对象,要求这个对象就已经被autorelease过了
+(instancetype)person
{
return [[[self alloc]init]autorelease];
}
这样,我们直接调用类方法,就可以得到1个已经被autorelease过的对象
@autoreleasepool
{
Person *p1 = [Person person];
//这个p1对象已经被autorelease过了,不需要再调用autorelease了
//这个p1对象就被存储到当前自动释放池之中
}// 当自动释放池结束,就会为存储在其中的p1对象发送release消息
6. 实际上Apple的框架中的类也是遵守这个规范的
通过类方法创建的对象都是已经被autorelease过的了,
所以,我们也要遵守这个规范,类方法返回的对象也要被autorelease过
以后,我们凡是创建对象是调用类方法创建的对象,这个对象就已经是autorelease过的了
2. ARC机制概述
1. 什么是ARC
Automatic Reference Counting 自动引用计数, 即ARC
顾名思义:系统自动的帮助我们去计算对象的引用计数器的值
可以说是WWDC2011和ios引入的最大的变革和最激动人心的变化
ARC是新的LLVM3.0编译器的一项特性, 使用ARC, 可以说一举解决了广大ios开发者所憎恨的手动管理内存的麻烦
在程序中使用ARC非常简单, 只需要像往常那样编写代码
只不过永远不要写retain release autorelease 永远要手动的调用 dealloc这三个关键字就好, 这是ARC的最基本原则
当ARC开启时, 编译器会自动的在合适的地方插入retain, release autorelease代码
编译器自动为对象做引用计数, 而作为开发者, 完全不需要担心编译器会做错,
需要特别注意的是:ARC是编译器机制, 在编译器编译代码的时候, 会在适当的位置加入retain, release 和autorelease代码
2. ARC机制下, 对象何时被释放
本质: 对象的引用计数器为0的时候, 自动释放
表象: 只要没有强指针指向这个对象, 这个对象就会立即被回收
3. 强指针与弱指针
强指针: 默认情况下, 我们声明1个指针, 这个指针就是1个强指针
我们也可以使用__strong来显示的声明这是个强指针
Person *p1;这是个强指针, 指针默认情况下都是1个强指针
__strong Person *p2; 这也是1个强指针, 使用 __strong来显示的声明强指针
弱指针: 使用__weak标识的指针就叫做弱指针
无论是强指针还是弱指针, 都是指针, 都可以用来存储地址, 这是没有区别的
都可以通过这个指针访问对象的成员
唯一的区别就是在ARC模式下, 他们用来作为回收对象的基准
如果1个对象没有任何强类型的指针指向这个对象的时候, 对象就会被立即被自动释放
4. 确认程序是否开启ARC机制
1). 默认情况下, Xcode开启ARC机制
2). ARC机制下, 不允许调用retain release retainCount autorelease方法
3). 在dealloc中, 不允许[super dealloc]
5. 演示1个ARC案例
int main(int argc, const char *argv[]){
@autoreleasepool{
Person *p1 = [Person new];// p1是1个强指针. 每个指针变量默认情况下都是1个强指针变量
NSLog(@"------");
}// 当执行到这里的时候, p1指针被回收, 那么Person对象就没有任何强指针指向它了, 对象就再这被回收
return 0;
}
3. ARC
1. ARC下的单个对象的内存管理
在ARC的机制下: 当1个对象没有任何的强指针指向它的时候, 这个对象就会被立即回收
1). 当指向对象的所有的强指针被回收的时候, 对象就会被立即回收
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];// p1是个强指针
Person *p2 = p1;// p2也是个强指针, p1和p2都指向Person对象
// 每个指针变量默认情况下都是1个强指针变量
NSLog(@"---");
}// 当执行到这里的时候, p1指针被回收, p2指针也被回收, 那么Person 对象就没有任何强指针指向它了, 对象就在这被回收
return 0;
}
2). 将所有指向对象的强指针赋值为nil的时候, 对象就会被立即回收
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];// p1是个强指针, 每个指针变量默认情况下都是强指针变量
p1 = nil; // 当执行到这句话的时候, p1赋值为nil, p1指针不再执行Person对象.
// Person对象没有被任何的指针所指向, 所以, Person对象在这里被释放
NSLog(@"----");
}
return 0;
}
这两种情况就叫做没有任何强指针指向对象
1). 指向对象的所有强指针被回收掉
2). 指向对象的所有强指针赋值为nil
2. 强指针与弱指针
1). 强指针与弱指针的声明
默认情况下, 所有的指针都是强类型的, 也就是说我们之前声明的指针变量都是强类类型的
p1指针是强类型的, 因为默认情况下指针都是强类型的
Person *p1 = [[Person alloc] init];
不过我们可以使用__strong来显示的标识指针是强类型指针
__strong Person *p2 = [Person new];
这个时候p2指针类型是强指针类型的, 其实写不写__strong都是强类型的指针
指针类型也可以是弱指针类型
使用__weak标识指针的类型是弱类型指针
__weak Person *p3 = p2;
这个时候, p3指针就是1个弱类型的指针, p3弱指针也指向p2指针指向的对象
在操作对象的时候, 通过强指针或者弱指针都可以操作, 没有任何区别
2). ARC模式下的对象回收标准
ARC机制下释放1个对象的标准是:没有任何强指针指向对象的时候, 对象就会被释放
如果这个时候有弱指针指向, 也会被释放
int main(int argc, const char * argv[]){
@autoreleasepool
{
//使用__strong来标识p1指针是强类型的, 其实不写__strong也是强类型的
__strong Person *p1 = [[Person alloc] init];
// 使用__weak标识指针p2的类型是弱类型指针
__weak Person *p2 = p1;
// 这个时候, p2指针和p1指针都指向Person对象
// 这个时候如果设置p1的值为nil
p1 = nil;
// 这个时候Person对象只有被1个弱指针p2指向, 没有任何强指针指向
// 所以Person对象在这里被回收
}
return 0;
}
3). 最重要的1点: 不能创建对象用1个弱指针存储这个对象的指针
这样的话, 刚创建出来的对象, 就没有任何强指针指向, 创建出来就会被回收
int main(int argc, const char * argv[])
{
@autoreleasepool
{
// 创建1个对象, 将这个对象的地址赋值给1个弱指针, 后果就是创建出来的这个对象没有被任何强指针指向, 刚创建出来就会被释放
__weak Person *p1 = [[Person alloc] init];
}
return 0;
}
4). 在ARC机制下, 当对象被回收的时候, 原来指向这个对象的弱指针会被自动设置为nil
3. ARC机制下的多个对象的内存管理
1.ARC机制下的对象的回收的标准, 当没有任何强类型的指针指向对象的时候, 这个对象就会被立即回收
2, 强类型指针, 弱类型指针
3. 什么情况下叫做对象没有强指针指向
1). 指向对象的强指针被回收
2). 指向对象的强指针被赋值为nil
4. 在ARC的机制下, @property参数不能使用retain
因为retain代表生成的setter方法是MRC的标准的内存管理代码, 而我们在ARC的机制下, 不需要这些代码的
所以, 在ARC机制下的setter方法, 什么都不要做, 直接赋值就可以了
5. ARC机制下, 我们关注的重点
当1个类的属性是1个OC对象的时候, 这个属性应该声明为强类型的还是弱类型的, 很明显, 应该声明为1个强类型的
如何控制@property生成的私有属性, 是1个强类型的还是1个弱类型的呢
使用参数, strong和weak
@property(nonatomic, strong)Car *car
代表生成的私有属性_car是1个强类型的
@property(nonamic, weak)Car *car;
代表生成的私有属性_car是1个弱类型的
如果不写, 默认是strong
6.使用建议
1).在ARC机制下, 如果属性的类型是OC对象类型的, 绝大多数场景下使用strong
2).在ARC机制下,如果属性的类型不是OC对象类型的,使用assign
3).strong和weak都是应用在属性的类型是OC对象的时候, 属性的类型不是OC对象的时候就使用assign
在ARC机制下,将MRC下的retain换位strong
@property(nonamic, strong)Car *car;
做的事情:
1), 生成私有属性, 并且这个私有属性是strong
2). 生成getter setter方法的声明
3) 生成getter setter方法的声明
setter 的实现:直接赋值
7. ARC机制下的循环引用
在ARC机制下,当两个对象相互引用的时候, 如果两边都使用strong, 那么就会出现内存泄漏
解决方案, 1端使用strong, 1端使用weak
8. @property参数总结
1. 开发程序分为ARC和MRC
2. 与多线程相关的参数
atomic 默认是安全的, 但是效率低下
nonatomic:不安全, 但是效率高
无论在ARC还是在MRC都可以使用
使用建议:无论是ARC还是MRC, 都使用nonatomic
3. retain:
只能在MRC的模式下, 代表生成的setter方法是标准的内存管理代码
当属性的类型是OC对象的时候, 绝大多数情况下使用retain, 只有在出现了循环引用的时候1边retain 1边assign
4. assign
在ARC和MRC的模式下都可以使用assign
当属性的类型是非OC对象的时候, 使用assign
5. strong:
只能使用在ARC机制下, 当属性的类型是OC对象类型的时候, 绝大多数情况下使用strong
只有出现了循环引用的时候, 1端strong 1端weak
6 weak:
只能在使用ARC机制下, 当属性的类型是OC对象的时候, 只有出现了循环引用的时候, 1端strong, 1端weak
7. readonly readwrite
无论是ARC还是MRC都可以使用
8. setter getter 无论在ARC下还是在MRC下都可以改
在ARC机制下, 原来使用retain的用strong
出现循环引用的时候, MRC: 1边retain, 1边assign, ARC: 1边strong, 1边weak
11. 分类
1. 分类。 类别, 类目, category
2. 如果将所有的方法都写在同1个类模板中, 当然是完全可以的,如果全都写在一个模板中, 就显得很臃肿, 后期难以维护和管理
默认情况下1个类独占1个模板, 这个是将所有的成员都写在这1个模板中, 我们的想法: 那就让1个类占多个模块, 将功能相似的方法定义在同1个模块中, 这样的好处:方便维护和管理
3. 分类:
1).顾名思义: 将1个类分为多个模块
2)。如何为1个类添加分类
3).会生成1个.h和1个.m的模块
模块的文件名: 本类名+分类名.h 本类名+分类名.m
4).添加的分类也分为声明和实现
@interface 本类名(分类名)
@end
代表不是新创建1个类,而是对已有的类添加1个分类. 小括弧中写上这个分类的名字. 因为1个类可以添加多个分类, 为了区分每个分类, 所以分类要取名字
@implementation Student(ss)
@end
这是分类的实现
4. 分类的使用
1). 如果要访问分类中定义的成员, 就要把分类的头文件引进来
5. 分类的作用:将1个类分为多个模块
使用分类注意的几个地方:
1.分类只能增加方法, 不能增加属性
2.在分类之中可以写@property但是不会自动生成私有属性, 也不会自动生成getter setter的实现
只会生成getter setter的声明
所以, 你就需要自己写getter和setter的实现, 也需要自己定义属性, 这个属性就必须在本类中
3. 在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)
但是可以调用本类的getter setter来访问属性
本类的@property生成的私有属性, 只可以在本类的实现中访问
分类中不能直接访问私有属性
分类可以使用getter setter 来访问
4. 分类中可以存在和本类同名的方法
当分类中有和本类中同名的方法的时候, 优先调用分类的方法, 哪怕没有引入分类的头文件
如果多个分类中有相同的方法, 优先调用最后编译的分类
什么时候需要使用分类
当1个类的方法很多很杂的时候, 当1个类很臃肿的时候
那么这个时候我们就可以使用分类, 将这个类分为多个模块, 将功能相似的方法写在同1个模块之中
14. 非正式协议
1. 分类的作用在于可以将我们写的类分为多个模块
可以不可以为系统的类写1个分类呢
为系统自带的类写分类, 这个就叫做非正式协议
2. 分类的第2个作用:
为1个已经存在的类添加方法
分类的作用:
1). 将臃肿的类分为多个管理, 方便管理
2). 扩展1个类