0
点赞
收藏
分享

微信扫一扫

自动引用技术

芭芭蘑菇 2021-09-19 阅读 66

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 数组 【暂略】


有关数组的内容, 以后再细看一下, 【暂略】

举报

相关推荐

0 条评论