类
回顾OC中的类NSObject
,继承于objc_object
,默认有一个isa
指针,占8字节,那么在swift
中是否相同呢?
打印一个类的内存大小
import Foundation
class Person {
}
print(class_getInstanceSize(Person.self))
print("Hello, World!")
16
Hello, World!
Program ended with exit code: 0
swift
中一个类在没有任何属性的情况下,默认占用16字节大小,那16字节是什么,打开编译好的底层代码
初始化方法 swift_allocObject
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast(
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);//初始化一个实例对象
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
进入 swift_slowAlloc
分配空间
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
void *p;
// This check also forces "default" alignment to use AlignedAlloc.
if (alignMask <= MALLOC_ALIGN_MASK) {
#if defined(__APPLE__)
p = malloc_zone_malloc(DEFAULT_ZONE(), size);
#else
p = malloc(size);// 堆中创建size大小的内存空间,用于存储实例变量
#endif
} else {
size_t alignment = (alignMask == ~(size_t(0)))
? _swift_MinAllocationAlignment
: alignMask + 1;
p = AlignedAlloc(size, alignment);
}
if (!p) swift::crash("Could not allocate memory.");
return p;
}
我们其实可以看到 swift_allocObject
返回的是一个叫做HeapObject
的东西,也就是说Swift
的类本质其实是HeapObject
HeapObject
是一个结构体,有两个属性,metadata
refCounts
其中
metadata
的类型是HeapMetadata
,是一个指针,占8字节
refCounts
(引用计数,类型是InlineRefCounts
,而InlineRefCounts
是一个类RefCounts
的别名,占8个字节)
Swift属性种类
- 存储属性
- 计算属性
- 延迟存储属性
- 类型属性
存储属性
- 常量属性 用
let
修饰let firstName:String = "hu"
- 可变属性 用
var
修饰var lastName:String = "ym"
计算属性
特点:不占用内存空间,本质就是set/get方法,由于不占用内存,如果实现set方法,给自己赋值会崩溃,可以自己尝试
class Person {
let firstName:String = "hu"
var lastName:String = "ym"
var name:String {
get {
return firstName + lastName
}
}
}
let person = Person()
print(person.name)
print(class_getInstanceSize(Person.self))
huym
48
Hello, World!
Program ended with exit code: 0
48 = metadata(8字节) +refCount(8字节)+ String(16字节)+ String(16字节)
String占用16字节与OC不同(以后分析完再补)
延迟属性
lazy var sex:String = "boy"
1.使用lazy
修饰的存储属性
2.必须有默认值
3.在第一次访问的时候才被赋值
4.有线程安全隐患
5.实例对象内存大小没有变化
在底层默认是optional,在没有被访问时,默认是nil,在内存中的表现就是0x0。在第一次访问过程中,调用的是属性的getter方法
类型属性
static var default: Person = Person()
1.使用static
修饰,并且是全局变量
2.必须有值
3.类型只会被初始化一次
源码底层调用swift_once
方法,使用了GCD
中的dispatch_once_f
,和OC中单利的实现一样,所以 swift中的单利可以直接使用类型属性
属性观察者(willSet 、didSet)
willSet
属性的值将要被修改时调用
didSet
属性的值将被修改后调用
init
初始化中给属性赋值,并不会触发观察者(???以后再补)
var lastName:String = "ym" {
willSet{
print("willSet oldValue \(lastName) newValue \(newValue) ")
}
didSet{
print("didSet oldValue \(oldValue) newValue \(lastName) ")
}
}
person.lastName = "ios"
willSet oldValue ym newValue ios
didSet oldValue ym newValue ios
有点类似OC里面的KVO,监听一个值的改变
情况1. 子类继承父类,同时都实现属性观察者,调用顺序是怎么样的
结论:先子类willset,后父类willset,在父类didset, 子类的didset
情况2. 父类在init中赋初始值,子类再次赋初始值,是否会触发观察者方法
结论:会