ARC(automatic reference counting)【 自动引用技术】:是指内存管理中对引用采取自动计数的技术。
类似的生活例子:办公司灯光的开启与关闭。
>1.2.2内存管理的思考方式
1、自己生成的对象,自己持有
1、 非自己生成的对象,自己也能持有
3、不再需要自己持有的对象时释放 (release)
4、无法释放非自己持有的对象
alloc/new/copy/mutableCopy 生成并持有的对象、或者使用了retain持有的对象, 不需要的时候需要释放。
此外得到的对象,所得的对象不需要释放。
eg: 释放了再次释放
1.2.3 alloc/retain/release/dealloc 实现 - gnustep
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、使用断点追踪程序的执行函数调用。
内存块头部管理引用计数的好处:
1)少量代码即可完成
2)能够统一管理引用计数用内存块与对象用内存块
通过引用基数表管理引用计数的好处:
1)对象用内存块的分配无需考虑内存块头部
2)引用计数表各记录中存有内存块地址,可从各个记录追溯到各对象的内存块。
3)利用工具检测内存泄漏时,引用计数表的各记录也有助于检测各对象的持有者是否存在。
// 这里就涉及到引用计数的实现内容, 这个地方要深入研究一下, runloop 之间和runtime之间的调用
1.2.5 autorelease (自动释放)
有关autoreleasepool这个功能的实现 objc-internal.h 这个文件里面可以看到头文件。 在NSObject里面已经实现了这个功能。 @autoreleasepool {} 这个里面,应该就是我们调用实现的过程了。
NSArray 中的array方法有关的内容
但是大量产生autorelease的对象时, 只要不废弃NSAutoreleasePool对象, 那么生成的对象就不能够释放,因此有时候生成内存不足的对象。典型的例子是读入大量图像的同事改变其尺寸。 对象文件读入到NSData对象,并从生成UIImage对象,改变该对象尺寸后生成新的UIImage对象。 这种情况下,就会大量产生autorelease的对象。
这种情况下,有必要在适当的地方生成、持有、或废弃NSAutoreleasePool对象。
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("##############");
}
autorelease 这个地方需要深入研究一下。
1.3 ARC 规则
编译器进行了ARC的内容处理,里面还是使用了“引用计式内存管理”
1.3.3 所有权修饰符
__strong
__weak
__unsafe_unretained
__autoreleasing
《1》__strong 修饰符
__strong 修饰的, 超出作用域范围之后,就会释放掉。
__strong 修饰符通后面要讲的__weak 修饰符 和__autoreleasing 修饰符一起,可以保证将附有的自动变量初始化为nil。
《2》__weak 修饰符
————> weak 若引用,避免了循环引用。
《3》__unsafe_unretained 修饰符 【不安全的所有权修饰符】
id __unsafe_unretained obj = [NSObject new];
//生产出有关的警告:
Assigning retained object to unsafe_unretained variable; object will be released after assignment
unsafe_retained 这个修饰符确保其变量的值是存在的。 否在造成程序奔溃。 而weak可以自动置为nil。
《4》__autorelease 修饰符
ARC 环境下使用:
@autoreleasepool { 。。。。 }
1.3.4 规则
1.3.5 属性
1.3.6 数组 【暂略】
有关数组的内容, 以后再细看一下, 【暂略】