2013我国中小企业接入互联网和网站建设情况,ci框架建设网站案例,扁平化网站布局,网站建设不包括哪个阶段文章目录 前言一、autorelease实现二、苹果的实现三、内存管理的思考方式__strong修饰符取得非自己生成并持有的对象__strong 修饰符的变量之间可以相互赋值类的成员变量也可以使用strong修饰 __weak修饰符循环引用 __unsafe_unretained修饰符什么时候使用__unsafe_unretained … 文章目录 前言一、autorelease实现二、苹果的实现三、内存管理的思考方式__strong修饰符取得非自己生成并持有的对象__strong 修饰符的变量之间可以相互赋值类的成员变量也可以使用strong修饰 __weak修饰符循环引用 __unsafe_unretained修饰符什么时候使用__unsafe_unretained __autoreleasing修饰符访问附有__weak 修饰符的变量时实际上必定要访问注册到autoreleasepool的对象 四、ARC规则不要显式调用dealloc 五、属性 前言
在学习ARC之前先来复习一下内存管理以及autorelease的实现
一、autorelease实现
先来看一下GNUstep源代码 autorelease其本质就是调用NSAutoreleasePool 对象的addObject 类方法就是将对象加到自动释放池中 接下来再看一下废弃自动释放池的一些功能函数
二、苹果的实现
可使用showPools输出现在的NSAutoreleasePool的状况输出到控制台 NSAutoreleasePool *pool [[NSAutoreleasePool alloc] init];id obj [[NSObject alloc] init];id obj2 [[NSObject alloc] init];id obj3 [[NSObject alloc] init];[obj autorelease];[obj2 autorelease];[obj3 autorelease];_objc_autoreleasePoolPrint();[pool drain];三、内存管理的思考方式
引用计数式内存管理的思考方式就是思考ARC所引起的变化
ARC有效时id 类型和对象类型同C语言其他类型不同其类型上必须附加所有权修饰符。 所有权修饰符一共有4 种
ARC环境下特有
__strong修饰符__weak修饰符__unsafe_unretained修饰符__autoreleasing修饰符
__strong修饰符
_ strong修饰符是id类型和对象类型默认的所有权修饰符 也就是说id obj [[NSObject alloc] init]; id __strong obj [[NSObject alloc] init];
再来看下面这段代码
//ARC有效
{id __strong obj [[NSObject alloc] init]
}此源代码指定了变量的作用域当obj超出其变量作用域时 obj会被废弃同时自动释放其被赋予的对象[[NSObject alloc] init]
而在MRC中等效的代码为
//ARC无效
{id obj [[NSObject alloc] init][obj release];
}因为ARC无效的时候obj超出变量作用域时变量并不会被自动废弃对象也会仍然存在需要我们手动减少对象的引用计数[obj release]去销毁对象
取得非自己生成并持有的对象
具体如下 这里需要注意的一点是此处obj确实持有了对象并且对象的引用计数为1但是在目前版本的Xcode的MRC环境中 {id obj [NSMutableArray array];NSLog(%lu, [obj retainCount]);}在MRC环境下输出的值应该为0因为array方法表明取得非自己生成并持有的对象也就是说obj并不持有对象但是输出如下 我们来解释一下输出为1的原因 当我们调用 retainCount 方法时,对于从自动释放池获取的对象,它会临时retained一次,以防止对象被过早释放而导致访问过期数据。 1.array方法创建了一个对象并将其加到自动释放池中此时retain count为0 2.obj指向自动释放池中的那个对象并没有对对象进行retain操作只是持有了一个指向他的指针 3.调用 [obj retainCount] 时 a.编译器会临时保留(retain)对象 b.获得并输出其retain count值 c.释放对象
所以尽管对象最初的 retain count 为 0,但由于 retainCount 方法的实现机制,它会临时保留对象来避免崩溃,这导致我们看到的输出 retain count 为 1。
__strong 修饰符的变量之间可以相互赋值 id __strong obj0 [[NSObject alloc] init];//对象Aid __strong obj1 [[NSObject alloc] init];//对象Bid __strong obj2 nil;obj0 obj1;//obj0持有有obj1赋值的对象B的强引用obj0被赋值。所以原先持有的a的强引用失效此时b的强引用变量为obj1obj0由此__strong 修饰符的变量不仅只在变量作用域中在赋值上也能够正确地管理其对象的所有者。
类的成员变量也可以使用strong修饰 重点是当Test对象释放时Test对象的obj_成员变量也会随之被释放
__weak修饰符
使用weak可以使我们取得对象但是并不持有对象 下面有一个代码例子进行解释 id a [[NSObject alloc] init];id __weak b a;id c a;NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)a));NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)c));a nil;NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)c));NSLog(%, b);NSLog(%, c);c nil;NSLog(%, b);NSLog(%, c);可以看到在ARC环境下a,b,c都指向了对象但是引用计数只有2这是因为weak是指向对象的指针但并不持有对象并不会使引用计数加1
当我们将weak修饰符改为strong时就会出现如下结果 id a [[NSObject alloc] init];id __strong b a;id c a;NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)a));NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)c));a nil;NSLog(%lu, CFGetRetainCount((__bridge CFTypeRef)c));NSLog(%, b);NSLog(%, c);c nil;NSLog(%, b);NSLog(%, c);循环引用
内存管理中会发生循环引用的问题此时就需要用到__weak修饰符
正确的内存释放过程 首先B对象是A对象的一个属性也就是A持有B现在要释放掉A需要给A发送一个release消息这时A的引用计数变为0就要走delloc方法delloc方法会对A所持有的全部对象发送release消息当然也包括B也就是对B进行release此时B的引用计数也变为0然后执行delloc最后A与B都被释放掉了
- (void)dealloc {[_b release]; // 释放持有的 B 实例_b nil;[super dealloc];
}循环引用的产生 解释对象之间互相持有形成闭环导致谁也无法被正确释放 循环引用容易发生内存泄漏。所谓内存泄漏就是应当废弃的对象在超出其生存周期后继续存在。
过程
a.例如我们现在想让A释放,也就是让B给A发送release消息此时B的属性强持有A,所以需要B在delloc方法中对A进行releaseb.我们想要让B执行delloc就需要就持有B的A对象发送release消息给Bc.想要A发送release消息给B就需要A执行delloc方法d.想要A执行delloc方法就需要持有A的B对象发送release消息
如此循环往复对象之间都在等对方给自己发送release消息导致谁也无法执行如此往复便造成了循环引用
当然循环引用并不只出现在变量中还出现在协议与block中后面在学习的过程中会专门写博客记录
接下来我们谈论一下如何解决这类问题
因为我们知道weak可以使变量取得但并不持有对象也就是说不会增加对象的引用计数我们将对象中的属性用weak修饰符修饰就可以解决这个问题 使用weak时因为变量不持有对象因此不会造成相互引用当对象释放后weak变量会自动置为nil也避免了野指针的情况
#pragma mark 持有对象的弱引用 MRC 在MRC下没有__weak这样的自动nil置化特性 使用weak持有某对象弱引用时对象被废弃弱引用变量自动只为nilid __weak obj1 nil;{id __strong obj0 [[NSObject alloc] init];obj1 obj0;;NSLog(%, obj1);}NSLog(%, obj1);__unsafe_unretained修饰符
__unsafe_unretained是一个不安全的所有权修饰符在MRC下使用来避免循环引用
但是与weak相比其会产生悬垂指针
因此我们在使用**__unsafe_unretained必须保证对象存在**
什么时候使用__unsafe_unretained
其与weak相比可能会有一些更好的性能追求极致性能便可以使用__unsafe_unretained修饰以及在早版本的iOS中需要使用__unsafe_unretained来代替__weak.比如我们在访问单例或者全局变量时就可以使用这个修饰
__autoreleasing修饰符
ARC中不能使用autorelease方法以及NSAutoreleasePool类但是实际上ARC有效时autorelease功能还是有作用的
指定“autoreleasepol 块”来替代“NSAutoreleasePool 类对象生成、持有以及废弃”这一范围
autoreleasepool{
id __autoreleasing obj [[NSObject alloc] init];
}为对象附加__autoreleasing修饰符代替autorelease方法等价于在ARC无效时调用autorelease即将对象注册到autoreleasepool 但是我们一般不会显式地添加__autoreleasing因为编译器会检查方法名是否以alloc/new/ copy/mutableCopy 开始如果不是则自动将返回值的对象注册到autoreleasepool。 例如
autoreleasepool f
id __strong obj [NSMutableArray array];
}访问附有__weak 修饰符的变量时实际上必定要访问注册到autoreleasepool的对象
因为weak修饰符只持有对象的弱引用因此访问引用对象时对象可能被遗弃。所以我们将对象注册到autoreleasepool可以确保对象存在 当将对象注册在autoreleasepool中autoreleasepool会临时保留这个对象直到作用域结束
另外在书上讲id *obj id __autoreleasing *obj,以此类推NSObject **obj便成为了NSObject * _autoreleasing *obj这里我们需要知道NSObject *__autoreleasing t1与NSObject __autoreleasing *t1有本质的不同
前者指向对象的对象会在被赋值时加入到自动释放池后者常在NSError错误处理中见到 在自动引用计数ARC管理的 Objective-C 环境中当你使用双重指针比如 NSError **error作为方法参数时ARC 会假定这个指针指向的对象是 __autoreleasing 总结来说NSObject *__autoreleasing t2 是一个自动释放的对象指针而 NSObject __autoreleasing *t1 是指向一个自动释放对象指针的指针。 我们以一个代码例子来实验一下
interface ViewController : UIViewController
property (nonatomic, weak)NSObject *Obj1;
property (nonatomic, weak)NSObject *Obj2;implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self test1];}- (void)test1 {NSLog(% %, self.Obj1, self.Obj2);[self test2];NSLog(% %, self.Obj1, self.Obj2);}- (void)test2 {NSObject *t1 [[NSObject alloc] init];NSObject *__autoreleasing t2 [[NSObject alloc] init];self.Obj1 t1;self.Obj2 t2;NSLog(% %, self.Obj1, self.Obj2);}这段代码中因为test2中t1超出变量作用域同时self.obj1是被weak修饰的并不持有对象对象超出作用域自动销毁因此第三行输出null。 而t2由于使用了 __autoreleasing,它的生命周期被延长到当前的自动释放池结束
它的生命周期被延长到当前的自动释放池结束这句话的含义是:
使用__autoreleasing修饰的对象不会在创建它的作用域(通常是一个函数或autoreleasepool块)结束时被立即释放。相反,这个对象会被自动添加到当前的Autorelease Pool中,延长了它的生命周期。直到当前的Autorelease Pool被销毁时,这个对象才会被最终释放。
interface ViewController : UIViewController
property (nonatomic, strong)NSObject *Obj1;
property (nonatomic, strong)NSObject *Obj2;implementation ViewController- (void)viewDidLoad {[super viewDidLoad];[self test1];}- (void)test1 {NSLog(% %, self.Obj1, self.Obj2);[self test2];NSLog(% %, self.Obj1, self.Obj2);}- (void)test2 {NSObject *t1 [[NSObject alloc] init];NSObject *__autoreleasing t2 [[NSObject alloc] init];self.Obj1 t1;self.Obj2 t2;NSLog(% %, self.Obj1, self.Obj2);}而这段代码中obj用了strong修饰即使t1,t2作为局部变量超出了作用域但是self.Obj1仍然持有这个对象因此这个对象并不会被销毁因此其仍然存在
四、ARC规则
不能使用retain/release/retainCount/autorelease不能使用NSAllocateObject/NSDeallocateObject必须遵守内存管理的方法名规则不要显式调用dealloc使用autorelease块代替NSAutoreleasePool不能使用区域NSZone对象型变量不能作为C语言结构体的成员显式转换id和void*
不要显式调用dealloc
dealloc 方法在大多数情况下还适用于删除已注册的代理或观察者对象。
五、属性