ARC(automatic reference counting)【 自动引用技术】:是指内存管理中对引用采取自动计数的技术。
类似的生活例子:办公司灯光的开启与关闭。
>1.2.2内存管理的思考方式
内存管理方法不包括在语言上,是包括在cocoa 框架中1)自己生成的对象,自己所持有
2)非自己生成的对象,自己也能持有
3)不再需要自己持有的对象时释放
4)非自己持有的对象无法释放
1、自己生成的对象,自己持有
id obj =[[NSObject alloc] init]
id obj = [NSObject new]
自己生成并持有对象, 自己持有对象。
copy/mutableCopy 方法生成并持有对象的副本。 和alloc/new 方法在“自己生成并持有对象” 这点上没有改变。
根据上述“使用以下名称开头的方法名”,下列名称具备同样的功能意义:
*) allocMyObject
*) newThatObject
*) copyThis
*)mutableCopyYourObject
但是以下开头的就不是同样的意义了
*) allocate
*) newer
*) copying
*)mutableCopyed
1、 非自己生成的对象,自己也能持有
取得非自己生成并持有的对象
id obj = [NSMutableArray array];
取得的对象存在,但自己不持有对象[里面包含了autorelease]
[obj retain]
自己持有对象, 通过retain方法
现在在ARC上面是否也已经实现了里面的功能。
为什么推荐使用alloc而不是new?
new = [alloc] init] ; 采用new的方式只能采用默认的init方法完成初始化,采用alloc的方式可以用其他定制的初始化方法。
3、不再需要自己持有的对象时释放 (release)
id obj = [[NSObject alloc] init] 自己生成并持有对象
[obj release] 释放对象
可以通过retain方法持有对象,一旦不再需要,务必用release方法进行释放。
alloc 生成并持有对象, 但是array中是如何处理呢?
array 方法是取得的对象存在,但自己不持有对象,又是如何实现的呢?
(id)object {
id obj = [[NSObject alloc] init];
[obj autorelease];
return obj;
}
autorelease 方法可以使取得的对象存在,但自己不持有对象。
autorelease 提供功能:使对象在超出指定的生存范围时能够自动并正确地释放(调用release方法)
NOTE: 这些取得谁都不持有的对象的方法名不能够以alloc/new/copy/mutableCopy 开头
4、无法释放非自己持有的对象
alloc/new/copy/mutableCopy 生成并持有的对象、或者使用了retain持有的对象, 不需要的时候需要释放。
此外得到的对象,所得的对象不需要释放。
eg: 释放了再次释放
1.2.3 alloc/retain/release/dealloc 实现 - gnustep
NSDefaultMallocZone , NSZoneMalloc 等名称中包含的NSZone是什么呢? 它是为了防止内存碎片化而引入的结构。 对内存分配的区域本身进行多重化管理, 根据使用对象的目的、对象的大小分配内存,从而提高了内存管理的效率。
struct obj_layout {
char padding[__BIGGEST_ALIGNMENT__ - ((UNP % __BIGGEST_ALIGNMENT__)
? (UNP % __BIGGEST_ALIGNMENT__) : __BIGGEST_ALIGNMENT__)];
gsrefcount_t retained; // 保存的引用技术,并将其写入对象内存头部,该对象内存块全部置0后返回。
};
typedef struct obj_layout *obj;
PS: GNUstep上实现的OC内容,是将引用技术存放在对象的头部进行管理的。
1.2.4 苹果的实现
1、使用断点追踪程序的执行函数调用。
方法调用栈
- alloc
+allocWithZone
class_createInstance 【objc-runtime-new.mm】
calloc
有关引用技术的方法调用, 有关源码在runloop里面找到
??? 这里面有一个疑惑了,为什么runtime里面有retaincount 调用的难道不是这样?
内存块头部管理引用计数的好处:
1)少量代码即可完成
2)能够统一管理引用计数用内存块与对象用内存块
通过引用基数表管理引用计数的好处:
1)对象用内存块的分配无需考虑内存块头部
2)引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块。
3)利用工具检测内存泄漏时,引用计数表的各记录也有助于检测各对象的持有者是否存在。
// 这里就涉及到引用计数的实现内容, 这个地方要深入研究一下, runloop 之间和runtime之间的调用
在 retain 方法加符号断点会发现 alloc, new, copy, mutableCopy 这四个方法都会通过 Core Foundation 的 CFBasicHashAddValue() 函数来调用 retain 方法。其实 CF 有个修改和查看引用计数的入口函数 __CFDoExternRefOperation,在 CFRuntime.c 文件中实现。 这个地方, 需要更加进一步去查看这个内容。
1.2.5 autorelease (自动释放)
(1) 看上去很想ARC, 但实际上它更类似于C语言中的自动变量(局部变量)的特性。
(2) 局部变量,定义的变量,超出了它的作用于,就会废弃。
autorelease 会像C语言的自动变量那样,来对待对象实例。 当超出其作用域时,对象实例的release实例方法被调用。 另外,
同C语言的自动变量不同的是, 变成人员可以设置变量的作用域 。
1)生成并持有NSAutoreleasePool对象
2)调用已分配对象的autorelease实例方法
3)废弃NSAutoreleasePool对象
对于所有调用过autorelease实例方法的对象,在废弃NSAutoreleasePool对象时, 都会调用release实例方法。
有关autoreleasepool这个功能的实现 objc-internal.h 这个文件里面可以看到头文件。 在NSObject里面已经实现了这个功能。 @autoreleasepool {} 这个里面,应该就是我们调用实现的过程了。
NSArray 中的array方法有关的内容
在Cocoa框架中, 相当于程序主循环的NSRunloop或在其他程序可运行的地方, 对NSAutoreleasepool对象进行生成、持有和废弃处理。 因此,应用程序开发者不一定非得使用NSAutoreleasePool 对象来进行开发工作。
但是大量产生autorelease的对象时, 只要不废弃NSAutoreleasePool对象, 那么生成的对象就不能够释放,因此有时候生成内存不足的对象。典型的例子是读入大量图像的同事改变其尺寸。 对象文件读入到NSData对象,并从生成UIImage对象,改变该对象尺寸后生成新的UIImage对象。 这种情况下,就会大量产生autorelease的对象。
这种情况下,有必要在适当的地方生成、持有、或废弃NSAutoreleasePool对象。
添加了释放的autoreleasepool 数组中经常会和这个有关系
1.2.6 autorelease 实现 —— GNUstep
也就是在GNUstep里面实现的autoreleasepool是使用链表的方式实现了autoreleasepool里面的嵌套关系。
@interface NSAutoreleasePool : NSObject
{
#if GS_EXPOSE(NSAutoreleasePool) && !__has_feature(objc_arc)
/* For re-setting the current pool when we are dealloc'ed. */
NSAutoreleasePool *_parent;
/* This pointer to our child pool is necessary for co-existing
with exceptions. */
NSAutoreleasePool *_child; // 这里管理的是有关的嵌套,这里看出来,嵌套是没有分支的
// 这里管理的是对象
/* A collection of the objects to be released. */
struct autorelease_array_list *_released;
struct autorelease_array_list *_released_head;
/* The total number of objects autoreleased in this pool. */
unsigned _released_count;
/* The method to add an object to this pool */
void (*_addImp)(id, SEL, id);
#endif
#if GS_NONFRAGILE
#else
/* Pointer to private additional data used to avoid breaking ABI
* when we don't have the non-fragile ABI available.
* Use this mechanism rather than changing the instance variable
* layout (see Source/GSInternal.h for details).
*/
@private id _internal GS_UNUSED_IVAR;
#endif
}
1.2.7 autoreleasepool实现 —— 苹果实现
// 基本的数据结构, pool里面就是使用了page对象,page是集成了pageData对象
class AutoreleasePoolPage;
struct AutoreleasePoolPageData
{
#if SUPPORT_AUTORELEASEPOOL_DEDUP_PTRS
struct AutoreleasePoolEntry {
uintptr_t ptr: 48;
uintptr_t count: 16;
static const uintptr_t maxCount = 65535; // 2^16 - 1
};
static_assert((AutoreleasePoolEntry){ .ptr = MACH_VM_MAX_ADDRESS }.ptr == MACH_VM_MAX_ADDRESS, "MACH_VM_MAX_ADDRESS doesn't fit into AutoreleasePoolEntry::ptr!");
#endif
magic_t const magic;
__unsafe_unretained id *next; // 这个应该也是指向的是当前的autoreleasepoolpage对象
pthread_t const thread;
AutoreleasePoolPage * const parent; // 父page
AutoreleasePoolPage *child; // 子page
// 可以看到这个也是一个量表
uint32_t const depth; // 表示的是什么深度
uint32_t hiwat; //?
// 初始化这个内容
AutoreleasePoolPageData(__unsafe_unretained id* _next, pthread_t _thread, AutoreleasePoolPage* _parent, uint32_t _depth, uint32_t _hiwat)
: magic(), next(_next), thread(_thread),
parent(_parent), child(nil),
depth(_depth), hiwat(_hiwat)
{
}
};
PS : 从这个结构可以看出来,
1》 pool里面的对象是使用链表实现
2》pool和pool之间的关系也是使用了链表的关系。
3》这里没有看出来,可以实现连个pool并列 ? 这个是为什么呢? 使用中,我们是可以并列的,为什么这个不行呢?难道这里使用了两个指针,里面的next和head
数据的继承类
//主要的两个方法
OBJC_EXPORT
void * _Nonnull
objc_autoreleasePoolPush(void)
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
OBJC_EXPORT
void
objc_autoreleasePoolPop(void * _Nonnull context)
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
// 下面两个方法是调用前面的两个方法
OBJC_EXPORT void * _Nonnull
_objc_autoreleasePoolPush(void)
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
OBJC_EXPORT void
_objc_autoreleasePoolPop(void * _Nonnull context)
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
OBJC_EXPORT void
_objc_autoreleasePoolPrint(void)
OBJC_AVAILABLE(10.7, 5.0, 9.0, 1.0, 2.0);
// 打印方法, 这个是调用了printAll 方法
__attribute__((noinline, cold))
static void printAll()
{
_objc_inform("##############");
_objc_inform("AUTORELEASE POOLS for thread %p", objc_thread_self());
AutoreleasePoolPage *page;
ptrdiff_t objects = 0; // 获取有关的对象
for (page = coldPage(); page; page = page->child) {
objects += page->next - page->begin();
}
_objc_inform("%llu releases pending.", (unsigned long long)objects); // 打印出来这个内容
if (haveEmptyPoolPlaceholder()) { // 没有了
_objc_inform("[%p] ................ PAGE (placeholder)",
EMPTY_POOL_PLACEHOLDER);
_objc_inform("[%p] ################ POOL (placeholder)",
EMPTY_POOL_PLACEHOLDER);
}
else {
for (page = coldPage(); page; page = page->child) {
// 变量里面嵌套的pool
page->print();
}
}
_objc_inform("##############");
}
image.png
image.png
autorelease 这个地方需要深入研究一下。
1.3 ARC 规则
编译器进行了ARC的内容处理,里面还是使用了“引用计式内存管理”
1.3.3 所有权修饰符
__strong
__weak
__unsafe_unretained
__autoreleasing
《1》__strong 修饰符
__strong 修饰符是id类型和对象类型默认的所有权修饰符。 "强引用"。持有强引用的变量在超出其作用域时被废弃,随着强引用的失效,引用的对象会随之释放。
id obj = [NSObject new];
_>
id __strong obj = [NSObject new];
__strong 修饰的, 超出作用域范围之后,就会释放掉。
还有特殊的:array] 方法
__strong 修饰符通后面要讲的__weak 修饰符 和__autoreleasing 修饰符一起,可以保证将附有的自动变量初始化为nil。
通过__strong 修饰符, 不必要再键入retain或者release ,完美地满足了“引用计数式内存管理的思考方式”。
因为id烈性和对象类型的所有权修饰符默认为__strong 修饰符, 所以不要写上__strong 。
《2》__weak 修饰符
(1) strong修饰符无法解决“循环引用”的问题。
(2) 循环引用容易发生内存泄漏。 所谓内存泄漏就是应当废弃的对象在超出其生存周期后继续存在。
(2)eg: 两个对象之间的相互应用, 自身引用自身。
————> weak 若引用,避免了循环引用。
没有强引用的弱引用,将会被释放掉
修改
使用weak修饰符可以避免循环应用,可以检查附有__weak 修饰符的变量是否为nil,可以判断被赋值的对象是否已废弃。
《3》__unsafe_unretained 修饰符 【不安全的所有权修饰符】
尽管ARC式的内存管理是编译器的工作,但附有__unsafe_unreatined 修饰符的变量不属于编译器的内存管理对象。
id __unsafe_unretained obj = [NSObject new];
//生产出有关的警告:
Assigning retained object to unsafe_unretained variable; object will be released after assignment
image.png虽然使用__unsafe_unretained 修饰符的变量同附有__weak修饰符的变量一样,因为自己生成并持有的对象不能够继续为自己所有,所以生成的对象会被立即释放。 到这里,__unsafe_unretained 修饰符和weak是一样的。
image.png
造成了野指针
unsafe_retained 这个修饰符确保其变量的值是存在的。 否在造成程序奔溃。 而weak可以自动置为nil。
《4》__autorelease 修饰符
ARC 环境下使用:
@autoreleasepool { 。。。。 }
因为没有显示指定所有权修饰符, 所以id obj同附有strong修饰符的id strong obj 是完全一样的。 由于return使得对象变量超出其作用域,所以,该强引用对应的自己持有的对象会被自动释放,但该对象作为函数的返回值,编译器会自动将其注册到autoreleasepool。
NSError **
这个内容,需要进一步去整理整理。
——————
1.3.4 规则
image.png1.3.5 属性
ARC 环境下1.3.6 数组 【暂略】
有关数组的内容, 以后再细看一下, 【暂略】
网友评论