美文网首页
swift基础_内存管理

swift基础_内存管理

作者: 李永开 | 来源:发表于2022-04-10 18:05 被阅读0次

一. 打印一个对象

我们经常打印对象的内存地址,也从网上看到一个对象占用16个字节,那到底是不是呢?
让我们先来打印一个对象

import Foundation

//UnsafeMutableRawPointer.allocate(byteCount: <#T##Int#>, alignment: <#T##Int#>)
class LYKClass {
    let age: Int = 11
}
var lykCls = LYKClass() //0x100b13090
let p1 = lykCls         //0x100b13090
let p2 = lykCls         //0x100b13090

print(MemoryLayout<LYKClass>.size)          //8
print(MemoryLayout<LYKClass>.stride)        //8
print(class_getInstanceSize(LYKClass.self)) //24 = (Swift.Object)16+Int(8)

let ptr = withUnsafeMutablePointer(to: &lykCls, {$0});
print(ptr)//0x00000001000081b8
print(ptr.pointee)//0x100b13090 ->ptr是一个指向lykCls对象的指针,ptr.pointee存的地址是lykCls的地址

使用lldb看下内存结构
我们看到有一个3好像是引用技术,有一个b是我们的age=11,看起来lykCls使用了24个字节,没有问题

(lldb) x/4wg 0x100b13090
0x100b13090: 0x0000000100008158 0x0000000600000003
0x100b130a0: 0x000000000000000b 0x000c000000000000

二. 挖一挖?

看起来和网上说的差不多,那我们往下挖一挖
看源码

1.
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
                                       size_t requiredSize,
                                       size_t requiredAlignmentMask) {
  assert(isAlignmentMask(requiredAlignmentMask));
  auto object = reinterpret_cast<HeapObject *>(
      swift_slowAlloc(requiredSize, requiredAlignmentMask));

  // NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
  // check on the placement new allocator which we have observed on Windows,
  // Linux, and macOS.
  new (object) HeapObject(metadata);
2.
struct HeapObject {
  /// This is always a valid pointer to a metadata object.
  HeapMetadata const *__ptrauth_objc_isa_pointer metadata;

  SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;

#ifndef __swift__
  HeapObject() = default;

  // Initialize a HeapObject header as appropriate for a newly-allocated object.
  constexpr HeapObject(HeapMetadata const *newMetadata) 
    : metadata(newMetadata)
    , refCounts(InlineRefCounts::Initialized)
  { }
  
  // Initialize a HeapObject header for an immortal object
  constexpr HeapObject(HeapMetadata const *newMetadata,
                       InlineRefCounts::Immortal_t immortal)
  : metadata(newMetadata)
  , refCounts(InlineRefCounts::Immortal)
  { }
3.
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS       \
  InlineRefCounts refCounts
4.
typedef RefCounts<InlineRefCountBits> InlineRefCounts;
5.
typedef RefCountBitsT<RefCountIsInline> InlineRefCountBits;
6.
class RefCountBitsT {

  friend class RefCountBitsT<RefCountIsInline>;
  friend class RefCountBitsT<RefCountNotInline>;
  
  static const RefCountInlinedness Inlinedness = refcountIsInline;

  typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::Type
    BitsType;
  typedef typename RefCountBitsInt<refcountIsInline, sizeof(void*)>::SignedType
    SignedBitsType;
  typedef RefCountBitOffsets<sizeof(BitsType)>
    Offsets;

  BitsType bits;

BitsType bits是RefCountBitsInt<refcountIsInline, sizeof(void)>的typename,所以说bits的大小=sizeof(void)=64
RefCount整个文件,就是对通过结构体RefCountBitOffsets对bits进行操作。
下面是RefCountBitOffsets的结构体元素

struct RefCountBitOffsets<8> {
  /*
   The bottom 32 bits (on 64 bit architectures, fewer on 32 bit) of the refcount
   field are effectively a union of two different configurations:
   
   ---Normal case---
   Bit 0: Does this object need to call out to the ObjC runtime for deallocation
   Bits 1-31: Unowned refcount
   
   ---Immortal case---
   All bits set, the object does not deallocate or have a refcount
   */
  static const size_t PureSwiftDeallocShift = 0;
  static const size_t PureSwiftDeallocBitCount = 1;
  static const uint64_t PureSwiftDeallocMask = maskForField(PureSwiftDealloc);

  static const size_t UnownedRefCountShift = shiftAfterField(PureSwiftDealloc);
  static const size_t UnownedRefCountBitCount = 31;
  static const uint64_t UnownedRefCountMask = maskForField(UnownedRefCount);

  static const size_t IsImmortalShift = 0; // overlaps PureSwiftDealloc and UnownedRefCount
  static const size_t IsImmortalBitCount = 32;
  static const uint64_t IsImmortalMask = maskForField(IsImmortal);

  static const size_t IsDeinitingShift = shiftAfterField(UnownedRefCount);
  static const size_t IsDeinitingBitCount = 1;
  static const uint64_t IsDeinitingMask = maskForField(IsDeiniting);

  static const size_t StrongExtraRefCountShift = shiftAfterField(IsDeiniting);
  static const size_t StrongExtraRefCountBitCount = 30;
  static const uint64_t StrongExtraRefCountMask = maskForField(StrongExtraRefCount);
  
  static const size_t UseSlowRCShift = shiftAfterField(StrongExtraRefCount);
  static const size_t UseSlowRCBitCount = 1;
  static const uint64_t UseSlowRCMask = maskForField(UseSlowRC);

  static const size_t SideTableShift = 0;
  static const size_t SideTableBitCount = 62;
  static const uint64_t SideTableMask = maskForField(SideTable);
  static const size_t SideTableUnusedLowBits = 3;

  static const size_t SideTableMarkShift = SideTableBitCount;
  static const size_t SideTableMarkBitCount = 1;
  static const uint64_t SideTableMarkMask = maskForField(SideTableMark);
};

从33-62位是强引用计数,通过计算器可以看到lykCls的引用计数是3

图片.png

通过函数打印也是3
print(CFGetRetainCount(lykCls as AnyObject)) //3

三. 那弱引用呢

我们使用weak修饰p3

weak var p3 = lykCls;

通过断点我们可以看到系统创建了一个WeakReference对象,并且调用nativeInit函数,将p3的地址传了进去

WeakReference *swift::swift_weakInit(WeakReference *ref, HeapObject *value) {
  ref->nativeInit(value);
  return ref;
}

接下来通过p3的引用计数对象的formWeakReference创建一个HeapObjectSideTableEntry对象side

void nativeInit(HeapObject *object) {
    auto side = object ? object->refCounts.formWeakReference() : nullptr;
    nativeValue.store(WeakReferenceBits(side), std::memory_order_relaxed);
  }

side的内部结构

class HeapObjectSideTableEntry {
std::atomic<HeapObject*> object;  //包裹的对象
BitsType bits;                                   //强引用信息
uint32_t weakBits;                          //弱引用信息
}

p3的内存地址

(lldb) x/3wg 0x0000000100b14000
0x100b14000: 0x0000000100008160 0xc000000020200b90
0x100b14010: 0x000000000000000b

p3的引用计数是:0xc000000020200b90,它是一个HeapObjectSideTableEntry对象的便宜地址

图片.png

将62 63位的值 置为0

图片.png

左移3位拿到0x101005C80即side的真实地址

图片.png

读取0x101005C80,看到了0x0000000600000003强引用地址,

图片.png

0x0000000100000002弱引用地址 这块不确定

相关文章

  • Swift 内存管理

    Swift 内存管理 [TOC] 前言 本文将介绍一下Swift中的内存管理,关于内存的一些基础知识可以参考我以前...

  • swift基础_内存管理

    一. 打印一个对象 我们经常打印对象的内存地址,也从网上看到一个对象占用16个字节,那到底是不是呢?让我们先来打印...

  • 每天学一点Swift----面向对象下(十)

    十九. Swift内存管理 1. Swift提供了强大的内存管理机制:Swift通过自动引用计数(ARC)可以很好...

  • Swift--内存管理

    Swift内存管理概述 强引用循环 打破强引用循环 闭包中的强引用循环 Swift内存管理概述 Swift中的AR...

  • Swift基础14(内存管理)

    跟OC一样,Swift也是采用基于引用计算的ARC内存管理方案(针对堆空间) Swift中ARC有3种引用 1、强...

  • swift内存管理

    Swift使用自动引用计数(ARC)机制来处理内存。通常情况下,Swift内存管理机制会自动管理内存,无须我们考虑...

  • Swift使用自动引用计数

    Swift使用自动引用计数(ARC)机制来处理内存。通常情况下,Swift内存管理机制会自动管理内存,无须我们考虑...

  • Swift底层原理-内存管理

    Swift底层原理-内存管理 Swift语言延续了和Objective-C语言一样的思路进行内存管理,都是采用引用...

  • Swift-内存管理,指针

    内存管理 Swift采用引用计数的ARC内存管理方案(堆空间) Swift的ARC中有3钟引用强引用弱引用(wea...

  • 内存管理

    Swift内存管理:管理引用类型的内存, 不会管理值类型, 值类型不需要管理内存管理原则: 当没有任何强引用指向对...

网友评论

      本文标题:swift基础_内存管理

      本文链接:https://www.haomeiwen.com/subject/gvyvsrtx.html