引自:《Objective-C高级编程 iOS与OS X多线程和内存管理》
//define OBJC_ARC_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode")))
- (instancetype)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (instancetype)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
这里的“计数”表明必然会有一个东西(变量)来记录引用的变化,而在OC里这个变量就是retainCount;那么还有一个问题就是通过什么方式来操作这个变量,OC里就是retain(引用次数加 1),release(引用计数减 1 )方法。
注:对象,指人可以识别的东西,具备属性、收发信息、处理信息;而从系统的角度看,操作对象就是操作一块内存。(可能不是很准确......)
alloc/new/copy/mutablecopy
(当然还有clloc...
等等),它们返回的都是指针,就是使用他们来生成对象并持有对象的。OC操作方法 | 对象的操作 | retainCount |
---|---|---|
alloc/new/copy/mutablecopy等 | 生成并持有对象 | 1? |
retain | 持有对象 | +1 |
release | 释放对象 | -1 |
dealloc | 销毁对象 | 此时该值没有意义 |
autorelease | 在自动释放池结束时,为里面的对象发送一条release消息 | (all object) -1 |
上面涉及的方法定义如下(NSOject.h):
//define OBJC_SWIFT_UNAVAILABLE(_msg) __attribute__((availability(swift, unavailable, message=_msg)))
+ (instancetype)new OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
- (void)dealloc OBJC_SWIFT_UNAVAILABLE("use 'deinit' to define a de-initializer");
- (id)copy;
- (id)mutableCopy;
NSAutoreleasePool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSString alloc] initWithstring:@"objective-c pool"];
[obj autorelease];
或[pool addObject:obj];
--- 1[pool drain];
等同于[pool release];
--- 2注意: 1 --> 建议使用autorelease方法,因为后面的方法会导致同一个对象被多次加入自动释放池中。
addObject:方法 2 --> 虽然两个方法效果等同,但还是建议使用自动释放池专门的drain方法。
drain方法
autorelease方法
选择Edit Scheme
勾选僵尸对象检测
只能用在调试阶段,值是不可靠的
tesh.m
main.m
指向异常的代码
补充:
持有对象
运行结果
疑问:mArrayCopy的retainCount是2 ?被持有者有两个?
再来一次release
从这里就可以证明了,cope出来的新对象只是被mArrayCopy自己所持有而已,所以当release一次的时候对象已经被释放了,如果再release就是野指针访问了(注:直接看持有者有多少)。
代码:
/**
* alloc就是分配内存的意思,返回了一个指向内存首地址的指针
*/
NSMutableArray *mArrayAlloc = [[NSMutableArray alloc] init]; // mArrayAlloc 持有对象
/**
* new 就相当于alloc+init,但是new有可能会返回同一个对象,所以并不建议使用
*/
NSMutableArray *mArrayNew = [NSMutableArray new]; // mArrayNew 持有对象
/**
* copy是一个实例方法,具体如下:
* - (id)copy
* Returns the object returned by copyWithZone:.
* - (id)copyWithZone:(NSZone *)zone
* Returns a new instance that’s a copy of the receiver.
* ---new instance 就表明了创建了一个新的内存,并返回首地址(id 相当于 void *)
*/
NSMutableArray *mArrayCopy = [mArrayAlloc copy]; //mArrayCopy 持有了对象
/**
* Returns the object returned by mutableCopyWithZone:.
* - (id)mutableCopy
* - (id)mutableCopyWithZone:(NSZone *)zone
* Returns a new instance that’s a mutable copy of the receiver.
* ---new instance 就表明了创建了一个新的内存,并返回首地址(id 相当于 void *)
*/
NSMutableArray *mArrayMutablecopy = [mArrayNew mutableCopy];//mArrayMutablecopy 持有了对象
源代码
运行结果
明显的野指针访问了
使用copy源代码
内容没有改变
Apple.h
Apple.m
Girl.h
Girl.m
main.m
如果要达到目的,apple让girl也持有,就要在girl得到apple的时候持有一下,而可以做持有操作的是retain,来看看:
内存泄漏
我们知道对象在最后销毁的时候是调用了dealloc方法的,那么girl既然持有了apple那么在销毁自己的时候是不是应该把自己持有的东西给交出来(释放掉),已死的对象不可能持有东西了吧,所以在girl的dealloc方法中加上apple释放的代码:
虽然上面的方法是可以的,但是有问题,问题如下:
retain
apple再持有一下[[Apple alloc] init],再给girl,直接翻译都是问题,而且从封装性来看,girl要持有apple应该是自己去持有,也就是要自己进行retain,而不是要apple先retain再给girl,
代码优化:
retain去掉
set方法中进行retain
还有,如果我们从现实生活中考虑问题(面向对象是现实世界的抽象),girl会不会只要一次apple呢?多要几个~~
为了防止内存泄漏,我得这么干,估计你看到这就想呵呵了:
正常释放
再次优化代码,目的是只要girl再次要一个新的apple就给它持有,如果是拿原来的apple当然不再次持有咯:
做if判断
正常释放
代码修改成下面这样就是真正的,girl直接的持有一个新的apple(新的内存空间)了,不过结果是一样的,正常释放: