gouxin

gogogo

  • 主页
  • 随笔
所有文章 友链 关于我

gouxin

gogogo

  • 主页
  • 随笔

swift之ARC

2018-08-07

swift的ARC

在swift中,一个对象在概念上有三种refcount。strong和unowned存在inlineref字段中,weak存在sidetable的refcount中。

  • Strong RC : 记录这个对象的强引用。当强引用为0时,这个对象被deinited。此时读取unowned reference会出错,读取weak reference会得到nil。Strong RC是通过额外引用计数extra记录的,也就是说,extra为0,代表实际的引用计数为1。
  • Unowned RC: 记录对象的无主引用。无主引用初始化为1。在deinite完成后-1,当无主引用为0时,这个对象分配的内存会被free掉。
  • weak RC: 记录这个对象的弱引用。弱引初始化也为1,在分配的内存free掉后-1。当弱引用为0,这个对象的side table entry会被释放掉。

对象初始化时是没有side table的。在下面情况下会生成:

  • 有弱引用
  • strong RC或者unowned RC计数溢出了
  • 有associated object
  • 等

强引用和无主引用指向这个对象。

弱引用指向这个对象的side table(声明一个weak变量相当于定义了一个WeakRefrence对象,这个对象会持有一个WeakReferenceBits, WeakReferenceBits是通过sidetableentry初始化的,sidetableentry包含了引用对象的指针和引用计数,所以当strongRC为0时,对象可以通过HeapObject的ref找到sidetableentry,并把其中的对象指针设置为nil,这样弱引用访问拿到的都是nil)。

swift中每个对象都是一个HeapObject。下面是他的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;//等价于isa
InlineRefCounts refCounts;//记录引用计数,并提供了操作引用计数的方法
#ifdef __cplusplus
HeapObject() = default;
// Initialize a HeapObject header as appropriate for a newly-allocated object.
constexpr HeapObject(HeapMetadata const *newMetadata)
: metadata(newMetadata)
, refCounts(InlineRefCounts::Initialized)
{ }
#ifndef NDEBUG
void dump() const LLVM_ATTRIBUTE_USED;
#endif
#endif // __cplusplus
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
HeapObject {
isa
InlineRefCounts {
atomic<InlineRefCountBits> {
strong RC + unowned RC + flags
OR
HeapObjectSideTableEntry*
}
}
}
HeapObjectSideTableEntry {
SideTableRefCounts {
object pointer
atomic<SideTableRefCountBits> {
strong RC + unowned RC + weak RC + flags
}
}
}
总的来说就是:InlineRefCounts会尝试自己处理引用计数,如果有side table了,那么会调HeapObjectSideTableEntry 的相关方法,HeapObjectSideTableEntry又调用SideTableRefCounts 的相关方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class WeakReference {
union {
std::atomic<WeakReferenceBits> nativeValue;
#if SWIFT_OBJC_INTEROP
id nonnativeValue;
#endif
};
void nativeInit(HeapObject *object) {
//初始化时通过refCount生成sidetable,并更改refCount的bit为sidetable的指针+flag
auto side = object ? object->refCounts.formWeakReference() : nullptr;
nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
}
....
}
class WeakReferenceBits {
uintptr_t bits;
....
WeakReferenceBits(HeapObjectSideTableEntry *newValue) {
setNativeOrNull(newValue);
}
.....
}

对象的生命周期状态机

  • Live (没有side table):

    对象的refcounts被初始化为:strong 1, unowned 1 ,weak 1。

    Strong varibale正常工作。

    unowned variable正常工作。

    没有weak variable。

    如果你声明了一个weak variable,那么会添加side table,对象变成Live with side table状态。

    当strong RC变为0了,会调用deinit(),然后对象状态变为DEINITING。

  • Live(有side table)

    weak variable正常工作。

    其他的和Live相同。

  • DEINITING(没有side table)

    deinit()正在进行。

    强引用变量将不能使用。

    使用unowned variable会引发swift_abortRetainUnowned() 错误。

    unowned variable的赋值正常工作。

    weak variable 不能使用。(因为没有side table就没有弱引用,所以也不存在使用弱引用变量)。

    weak variable的赋值会存为nil。

    当deinit()完成后,会调用swift_deallocObject 。swift_deallocObject 会调用canBeFreedNow() 来检查还有没有weak或者unowned references。如果canBeFreedNow返回yes,那么这个对象被free掉,对象变为DEAD,否则,将减少无主引用,对象变为DEINITED状态。

  • DEINITING有side table

    weak varibale为nil。canBeFreedNow 会一直返回false,所以这种情况不可能直接变为DEAD。

    其他同上。

  • DEINITED没有side table。

    deinit()完成了,不过无主引用unowned references还存在。

    Strong variable的操作(store和load)不可能发生。

    Unowned varibale的store不可能发生。

    Unowned varibale的load会报swift_abortRetainUnowned() 。

    weak variable的操作不能执行。

    如果unowned RC为0了,对象会被free掉,然后变为DEAD

  • DEINITED有side table。

    weak variable的load会返回nil。

    Weak variable的store不可能发生。

    当unowned RC为0时,对象被free掉。减少weak RC, 对象变为FREED。

    其他的同上。

  • FREED没有side table

    不可能发生,因为FREED需要weak RC, 有weak RC就一定有side table。

  • FREED有side table。

    对象虽然free了,但是指向side table的weak references还存在。

    strong variable的操作不可能发生。

    unowned variable的操作不可能发生。

    weak variable的load会返回nil。

    Weak variable的store不可能发生。

    当weak RC为0时,side table entry会free掉,对象变为DEAD

  • DEAD

    对象和他的side table都释放了。

循环强引用

循环强引用,导致了strongRC永远都不会为0,所以对象永远也不会deinit,也就不会free,sidetable有的话也不会释放掉。

我们可以使用weak或者unowned来打破,那什么时候使用呢?其实根据weak和unowned的特性,我们就能知道:

  • weak会在对象deinit后将sidetable中的对象指针置为nil,所以我们用weak引用不会出现访问野指针的情况,不过缺点也很明显,用了weak,引用计数就需要通过sidetable来记录,分配了更多的空间(sidetableentry),调用的层级也深一点(ref的操作最终都交给了sidetableentry)。
  • unowned在对象deinit后,访问对象将会崩溃,不过好处是无主引用记录在inlineref中,不会增加额外的东西(引用计数溢出了例外哈)

ok,结论就是如果你非常确定引用的对象在使用期间一直存在的,那么就用unowned。

如果你不太确定或者对象就是可能在使用期间已经被释放掉了,那么使用weak。

赏

谢谢你请我吃糖果

  • swift

扫一扫,分享到微信

微信分享二维码
swift之Mirror
test
© 2018 gouxin
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链
  • 关于我

tag:

  • iOS
  • git
  • android
  • 闲聊
  • ios wkwebview
  • ios问题
  • rxswift,rxcocoa
  • rxswift
  • shell
  • spring java
  • swift
  • ios
  • video
  • ffmpeg
  • appstore
  • springmvc
  • springmvc java

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 友情链接1
  • 友情链接2
  • 友情链接3
  • 友情链接4
  • 友情链接5
  • 友情链接6
很惭愧

只做了一点微小的工作
谢谢大家