/*
1. 自动释放池的原理.
存入到自动释放池中的对象,在自动释放池被销毁的时候.会自动调用存储在该自动释放池中的所有对象的release方法.
可以解决的问题:
将创建的对象,存入到自动释放池之中. 就不再需要手动的relase这个对象了.
因为池子销毁的时候 就会自动的调用池中所有的对象的relase。
2. 如何创建自动释放池.
@autoreleasepool
{
}
这对大括弧代表这个自动释放池的范围.
3. 如何将对象存储到自动释放池之中
在自动释放池之中调用对象的autorelease方法.就会将这个对象存入到当前自动释放池之中.
这个autorealse方法返回的是对象本身. 所以,我们可以这么写
@autoreleasepool
{
Person *p1 = [[[Person alloc] init]autorelease];
}
这个时候,当这个自动释放池执行完毕之后,就会立即为这个自动释放池中的对象发送1条release消息.
目前为止,我们感受到得autorelase的好处:
创建对象,调用对象的autorelase方法 将这个对象存入到当前的自动释放池之中.
我们就不需要再去relase 因为自动释放池销毁的时候 就会自动的调研池中所有对象的relase
4. 使用注意
1). 只有在自动释放池中调用了对象的autorelease方法,这个对象才会被存储到这个自动释放池之中.
如果只是将对象的创建代码写在自动释放之中,而没有调用对象的autorelease方法.是不会将这个对象存储到这个自动释放池之中的.
2). 对象的创建可以在自动释放池的外面,在自动释放池之中,调用对象的autorelease方法,就可以将这个对象存储到这个自动释放池之中.
3). 如果对象的autorelease方法的调用放在自动释放池的外面,是无法将其存储的这个自动释放池之中的.
autorelease 的调用只有放在自动释放池之中 才可以讲其存储到自动释放池之中,对象的创建可以在外面
4). 当自动释放池结束的时候.仅仅是对存储在自动释放池中的对象发送1条release消息 而不是销毁对象.
5). 如果在自动释放池中,调用同1个对象的autorelease方法多次.就会将对象存储多次到自动释放池之中.
在自动释放池结束的时候.会为对象发送多条release消息.
所以,1个自动释放池之中,只autorelease1次,只将这个对象放1次, 否则就会出现野指针错误.
6). 如果在自动释放池中,调用了存储到自动释放中的对象的release方法.
在自动释放池结束的时候,还会再调用对象的release方法.
这个时候就有有可能会造成野指针操作.
7). 将对象存储到自动释放池,并不会使对象的引用计数器+1 所以其好处就是:创建对象将对象存储在自动释放池,就不需要在写个release了.
8). 自动释放池可以嵌套.
调用对象的autorelease方法,会讲对象加入到当前自动释放池之中
只有在当前自动释放池结束的时候才会像对象发送release消息.
5. autorelease的应用场景.
0). 创建对象,将对象存储到自动释放池之中. 就不需要再去手动的realse。
1). 我们一般情况下,会为我们的类写1个类方法,用来让外界调用类方法来快速的得到1个对象.
规范:使用类方法得到的对象,要求这个对象就已经被autorelease过了.
提供1个类方法来快速的得到1个对象.
规范
a. 这个类方法以类名开头. 如果没有参数就直接是类名 如果有参数就是 类名WithXX:
b. 使用类方法得到的对象,要求这个对象就已经被autorelease过了.
+ (Person *)person
{
return [[[Person alloc] init]autorelease];
}
这样,我们直接调用类方法.就可以得到1个已经被autorelease过的对象.
@autoreleasepool
{
Person *p1 = [Person person];
//这个p1对象已经被autorelase过了.不需要再调用autorelase
//这个p1对象就被存储到当前自动释放池之中.
}//当自动释放池结束.就会为存储在其中的p1对象发送release消息.
2). 完善类方法.
当Person拥有1个子类Student. 这个Student子类也继承了父类的person类方法.
但是.通过子类Student调用person类方法能创建1个子类Student对象吗?
答案是否定的,因为person方法写死了,我们返回的就是1个Person对象.
我们的需求:
通过父类Person调用这个类方法,返回的是Person对象,通过子类Student调用这个person类方法返回的是Student对象.
a. 类方法person的返回值肯定不能写Person *了.我们首先想到了使用万能指针id.
b. 类方法的内部代码:
return [[[Person alloc] init]autorelease];
就不能这样写了,因为这样仍然是创建Person对象.
改成.
return [[[self alloc] init]autorelease];
在类方法中self代表当前类.通过那1个类调用这个方法self就是指的那1个类.
所以,这个时候,通过Person类调用person方法 self就指Person类.创建的就是Person对象.
通过Student来调用person方法,self就指Student类 创建的就是Student对象.
多爽啊!
代码改成如下:
+ (id)person
{
return [[[self alloc] init]autorelease];
}
这样貌似就可以了 完美.
3). 继续完善类方法.
上面写的方法,其实已经可以实现我们的需求了.
但是存在1个小瑕疵.
类方法person返回的类型是1个id类型.
id类型是1个无类型的指针. 其优点在于可以指向任何对象,编译器都不会报错.并且编译器不会做编译检查.
所以,我们在创建对象的时候:
Person *p1 = [Personperson]; 这样是没有问题的 因为创建的是Person对象 赋值给1个Person指针.
Student *s1 =[Student person]; 这样也是没有问题 因为创建的是Student对象,赋值给1个Student指针.
但是,各位,下面这样写:
NSString *str = [Person person];
你会发现这样是不出错的.编译器根本都不会警告.实际也是可以运行的.
原因在于person类方法返回的是1个无类型的指针.
所以这样明显是不允许的,但是编译器却步报错,甚至警告都不报.
为了解决这个问题. 方法的返回值我们使用instancetype
instancetype 只能作为方法的返回值.代表返回的是当前类的对象.
这是1个有类型的指针.
所以 当你将其赋值给1个类型不同的指针变量时 编译器就会报警告.
+ (instancetype)person
{
return [[[self alloc] init]autorelease];
}
6. 实际上Apple的框架中的类也是遵守这个规范的.
通过类方法创建的对象都是已经被autorelease过的了.
所以,我们也要遵守这个规范. 类方法返回的对象也要被autorealse过.
以后,我们凡事创建对象是调用类方法创建的对象 这个对象已经是被autorelease过的了.
*/
/*
1. 什么是ARC
Automatic ReferenceCounting,自动引用计数. 即ARC.
顾名思义:系统自动的帮助我们去计算对象的引用计数器的值,
可以说是WWDC2011和iOS5引入的最大的变革和最激动人心的变化.
ARC是新的LLVM3.0编译器的一项特性,使用ARC,可以说一举解决了广大iOS开着所憎恨的手动管理内存的麻烦.
在程序中使用ARC非常简单,只需要像往常那样编写代码.
只不过永远不要写retain、release、autorelease这三个关键字就好,这是ARC的最基本的原则.
当ARC开启时, 编译器会自动的在合适的地方插入retain、release、autorelase代码.
编译器自动为对象做引用计数. 而作为开发者,完全不需要担心编译器会做错(除非开发者自己错用了ARC).
需要特别注意的是: ARC是编译器机制. 在编译器编译代码的时候,会在适时的位置加入retain、release和autorealse代码.
2. ARC机制下,对象何时被释放
只要没有强指针指向这个对象,这个对象就会立即回收.
3. 强指针与弱指针.
强指针: 默认情况下,我们声明1个指针 这个指针就是1个强指针.
我们也可以使用__strong来显示的声明这是1个强指针.
Person *p1; 这是1个强指针. 指针默认情况下都是1个强指针.
__strong Person*p2; 这也是1个强指针.使用__strong来显示的声明强指针.
弱指针: 使用__weak标识的指针就叫做弱指针.
无论是强指针还是弱指针,都是指针,都可以用来存储地址,这1点没有任何区别 。
都可以通过这个指针访问对象的成员.
唯一的区别就是在ARC模式下.他们用来作为回收对象的基准.
如果1个对象没有任何强类型的指针指向这个对象的时候,对象就会被自动释放
4. 确认程序是否开启ARC机制.
1).默认情况下,Xcode开启ARC机制.
2).ARC机制下,不允许调用retain、relase、retainCount、autorelease方法.
3).在dealloc中 不允许[superdealloc];
5. 演示第1个ARC案例
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
NSLog(@"------");
}//当执行到这里的时候.p1指针被回收,那么Person对象就没有任何
//强指针指向它了. 对象就在这被回收.
return 0;
}
6. ARC下的单个对象的内存管理.
1). 当指向对象的所有的强指针被回收的时候,对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
Person *p2 =p1;//p2也是个强指针.p1和p2都指向Person对象.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
NSLog(@"------");
}//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何
//强指针指向它了. 对象就在这被回收.
return 0;
}
2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
p1 = nil;//当执行到这句话的时候.p1赋值为nil.
//p1指针不再执行Person对象.
//Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.
NSLog(@"------");
}
return 0;
}
7. 强指针与弱指针.
1). 强指针与弱指针的声明.
p1指针是强类型的,因为默认情况下指针都是强类型的.
Person*p1 = [[Person alloc] init];
不过我们可以使用__strong来显示的标识指针是强类型指针.
__strong Person *p2 = [Person new];
这个时候p2指针类型是强指针类型的.其实写不写__strong都是强类型的指针.
指针类型也可以是弱指针类型.
使用__weak标识指针的类型是弱类型指针.
__weakPerson *p3 = p2;
这个时候,p3指针就是1个弱类型的指针. p3弱指针也指向p2指针指向的对象.
在操作对象的时候,通过强指针或者弱指针都可以操作,没有任何区别.
2). ARC的对象回收标准
ARC机制下释放1个对象的标准是: 没有任何强指针指向对象的时候,对象就会被释放.
如果这个时候有弱指针指向,也会被释放.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
//使用__strong来标识p1指针是强类型的,其实不写__strong也是强类型的.
__strongPerson *p1 = [[Person alloc] init];
//使用__weak标识指针p2的类型是弱类型指针.
__weakPerson *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个弱指针
//后果就是创建出来的这个对象没有被任何强指针指向.
//刚创建出来就会被释放.
__weakPerson *p1 = [[Person alloc] init];
}
return 0;
}
*/
/*
1. ARC下的单个对象的内存管理.
1). 当指向对象的所有的强指针被回收的时候,对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
Person *p2 =p1;//p2也是个强指针.p1和p2都指向Person对象.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
NSLog(@"------");
}//当执行到这里的时候.p1指针被回收,p2指针也被回收.那么Person对象就没有任何
//强指针指向它了. 对象就在这被回收.
return 0;
}
2).将所有指向对象的强指针赋值为nil的时候.对象就会被立即回收.
int main(int argc, const char * argv[])
{
@autoreleasepool
{
Person *p1 = [Person new];//p1是1个强指针.
//因为我们说过,每1个指针变量默认情况下都是1个强指针变量.
p1 = nil;//当执行到这句话的时候.p1赋值为nil.
//p1指针不再执行Person对象.
//Person对象没有被任何的指针所指向,所以.Person对象在这里被释放.
NSLog(@"------");
}
return 0;
}
7. 强指针与弱指针.
1). 强指针与弱指针的声明.
默认情况下,所有的指针都是强类型的,也就是说我们之前声明的指针变量都是强类类型的
p1指针是强类型的,因为默认情况下指针都是强类型的.
Person *p1 = [[Person alloc]init];
不过我们可以使用__strong来显示的标识指针是强类型指针.
__strong Person *p2 = [Personnew];
这个时候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也是强类型的.
__strongPerson *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;
}
*/
/*
0. 在ARC机制下,set方法的封装不需要retainrelase
直接赋值就可以了.
1. 当对象的属性是另外1个属性的时候,这个属性应该是1个强指针还是弱指针?
肯定需要是1个强指针.
当1个强指针指向1个对象的时候.
说明我在用这个对象,这个对象就不能被销毁.
当我被销毁的时候 我的强指针也被销毁了 说明我现在不用你了.
对象再判断是否还有其他强指针指向它.以决定是否回收.
说那么多的废话.
只有1个结论.在ARC机制下,如果1个对象的属性是1个OC对象类型的 这个属性应该声明为1个强指针.
2. 需要注意的是.如果对象没有被任何强指针指向.那么对象就会被回收.
原来指向对象的弱指针的值会自动设置为nil
3. 使用@property如何让生产的属性是1个强类型或者弱类型.
使用参数. strong、weak
//@property参数strong 代表生成的私有属性_dog指针是1个强类型的指针.
@property(nonatomic,strong)Dog *dog;
//@property参数weak 代表生成的私有属性_name指针是1个弱类型的指针.
@property(nonatomic,weak)NSString *name;
默认的参数是strong。
4. @property参数注意.
1).在ARC机制下.@property参数retain就无法再使用了.因为retain参数代表的意义是生成的set方法是标准的MRC下的内存管理代码.
而ARC下,不需要引用记数. 就不需要生成这些内存管理代码了.
2).在ARC机制下,属性的类型是OC对象类型的时候,一般使用strong. 非OC对象的类型使用assign
3).strong、weak都是针对OC对象类型的属性,非OC对象不能使用strong、weak
*/
/*
1. 当两个类相互引用作为对方的属性的时候.
在ARC机制下. 如果两边的@property都是以strong 就会出现循环引用而导致的内存泄露
2. 解决方案:
一端使用strong 一端使用weak
*/
/*
1. 程序的内存管理模式.分为两种. MRC和ARC
2. 与多线程相关的参数.
atomic: 默认值
nonatomic
无论是在ARC还是MRC模式下,都使用nonatomic
3. retain: 只能使用在MRC模式下. 当属性的类型是1个OC对象的时候,除了循环引用之外,全部都是使用retain。
assign: 既可以用在MRC中,也可以用在ARC之中,
在MRC模式中和ARC模式下. 当属性的类型是1个非OC对象类型的时候. 使用assign
在MRC模式下,如果出现循环引用. 一端使用retain 一端使用assign
4. strong: 只能使用在ARC模式下,当属性的类型是1个OC对象的时候,除了循环引用之外,全部都是使用strong
weak: 只能使用在ARC模式下,当出现循环引用的时候. 一端使用strong 一端使用weak
5. readwrite: 可以使用在ARC模式和MRC模式下,代表同时生成属性的getter、setter
readonly: 可以使用在ARC模式和MRC模式下 代表只成属性的getter
6. 修改生成的getter setter的名字: 在ARC和MRC中都可以使用.
---------------------
@property参数: 在MRC中使用retain的 用strong来代替就可以了.
*/
/*
1 想象这样1个画面:
你要使用别人的类,但是别人的类是在MRC模式下得. 而你的程序是ARC模式.
2. 在ARC的程序中,有一些类使用MRC实现的.
如果希望某些指定的类还是使用MRC. 那么这个时候可以在
点击项目 -> 选中要改的Targer -> Bulid Phases -> ComplileSource
在这里双击要修改模式的类.
输入: -fno-objc-arc
就代表这个类使用的是MRC
*/
/*
转换的方式非常简单和粗暴:
就是简单的替换:
删除所有的 retain relase autorease [super dealloc]; retain strong
所以是有可能会出错得.
*/