你知道 alloc init new release 这些方法都干了什么吗?
一个简单的对象创建方式为 [[NSObject alloc] init] 。其中 alloc 和 init 都做了什么呢? 或者说 [NSObject new]
alloc:
先写结论,做了三件事:
- 分配内存空间,生成实例对象
- 初始化实例对象isa
- 判断实例对象是否存在c++析构函数,如果有,则调用
开始看代码,找到 NSObject.mm文件
+ (id)alloc {
return _objc_rootAlloc(self);
}
id _objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ objc 2.0版本
// #define LLVM_LIKELY fastpath 代表大概率会走这个方法
// #define LLVM_UNLIKELY slowpath 代表大概率不走这个方法
if (slowpath(checkNil && !cls)) return nil;
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// 这部分省略
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
_objc_rootAllocWithZone 这个方法做了什么呢?(此时取的函数为的是objc-runtime-new.mm (objc-runtime-old.mm 不考虑))
NEVER_INLINE
id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
// allocWithZone under __OBJC2__ ignores the zone parameter
return _class_createInstanceFromZone(cls, 0, nil,
OBJECT_CONSTRUCT_CALL_BADALLOC);
}
static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
int construct_flags = OBJECT_CONSTRUCT_NONE,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
ASSERT(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size;
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
// 动态分配内存空间生成实例(都是c++内存分配的方法)
if (zone) {
obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (slowpath(!obj)) {
if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
return _objc_callBadAllocHandler(cls);
}
return nil;
}
// 下面两个初始化 Isa初始化 都最终会调用objc_object::initIsa方法 在这就不展示initIsa方法
if (!zone && fast) {
//initIsa(cls, true, hasCxxDtor);
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// initIsa(cls, false, false);
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
// Recursively call C++ constructors on obj, starting with base class's
// ctor method (if any) followed by subclasses' ctors (if any), stopping at cls's ctor (if any).
// 翻译一下: 递归调用obj上的c++构造函数,从基类的ctor方法(如果有的话)开始,然后是子类的ctor方法(如果有的话),直到cls的ctor为止(如果有的话)。
// object_cxxConstructFromClass 就不详细展示了
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
拓展: isa_t 为一个联合体,所有信息公用一块内存,起到节省内存的作用。
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
在这里拓展一下 ISA_BITFIELD 部分,方便下面release方法的理解,引用 iOS概念攻坚之路(三):内存管理 中的 NONPOINTER_ISA 部分
ISA_BITFIELD 部分
// 以x86_64 架构 为例子
uintptr_t nonpointer : 1; // 0:普通指针,指向class,1:优化过,使用位域存储更多信息
uintptr_t has_assoc : 1; // 对象是否含有或曾经含有关联引用
uintptr_t has_cxx_dtor : 1; // 表示是否有C++析构函数或OC的dealloc
uintptr_t shiftcls : 44; // 存放着 Class、Meta-Class 对象的内存地址信息
uintptr_t magic : 6; // 用于在调试时分辨对象是否未完成初始化
uintptr_t weakly_referenced : 1; // 是否被弱引用指向
uintptr_t deallocating : 1; // 对象是否正在释放
uintptr_t has_sidetable_rc : 1; // 是否需要使用 sidetable 来存储引用计数
uintptr_t extra_rc : 8; // 引用计数能够用 8 个二进制位存储时,直接存储在这里
init: 从代码上看是返回了当前的对象
NSObject.mm 文件:
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
// In practice, it will be hard to rely on this function.
// Many classes do not properly chain -init calls.
return obj;
}
【注】意味着对于NSObject来说,多次调用init方法还是返回本身。
new:
相当于 [[NSObject alloc] init]]
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
看了源码来让我们验证一个事情,[NSObject alloc] 和 [[NSObject alloc] init] 是否是同一个对象,如图
果然是同个对象,但是在实际写代码的时候 不建议直接写 [Class alloc] 的方式。
顺便补充一下在类方法中的self和实例方法中的self的区别
为啥都是self,第一眼认为是同一个self
+ (id)alloc {
return _objc_rootAlloc(self);
}
- (id)init {
return _objc_rootInit(self);
}
其实在类方法中的self代表的是class,对象方法中的self代表的是当前对象。
测试一下
+(void)testClassFunc {
self;
}
-(void)testInstanceFunc {
self;
// [self class]; 等价于 +(void)testClassFunc中的self
}
通过clang编译之后的cpp文件找到对应的方法,从传参就能看出两者的区别。
我们可以看到在类方法中self的类型为Class,示例对象方法中类型为对象。
static void _C_Test_testClassFunc(Class self, SEL _cmd) {
self;
}
static void _I_Test_testInstanceFunc(Test * self, SEL _cmd) {
self;
}
我们都知道object对象释放的时候,在mrc中是通过release方法去释放对象,那release的时候具体做了些什么呢?
- (oneway void)release {
_objc_rootRelease(self);
}
NEVER_INLINE void
_objc_rootRelease(id obj)
{
ASSERT(obj);
obj->rootRelease();
}
ALWAYS_INLINE bool
objc_object::rootRelease()
{
return rootRelease(true, false);
}
ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, bool handleUnderflow)
{
if (isTaggedPointer()) return false;
bool sideTableLocked = false;
isa_t oldisa;
isa_t newisa;
retry:
do {
oldisa = LoadExclusive(&isa.bits);
newisa = oldisa;
// 当前 nonpointer 都为1 所有这个判断里面的方法不会执行
if (slowpath(!newisa.nonpointer)) {
ClearExclusive(&isa.bits);
if (rawISA()->isMetaClass()) return false;
if (sideTableLocked) sidetable_unlock();
return sidetable_release(performDealloc);
}
// don't check newisa.fast_rr; we already called any RR overrides
uintptr_t carry;
newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry); // extra_rc--
if (slowpath(carry)) {
// don't ClearExclusive()
goto underflow;
}
} while (slowpath(!StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits)));
if (slowpath(sideTableLocked)) sidetable_unlock();
return false;
underflow:
// newisa.extra_rc-- underflowed: borrow from side table or deallocate
// abandon newisa to undo the decrement
newisa = oldisa;
if (slowpath(newisa.has_sidetable_rc)) {
if (!handleUnderflow) {
ClearExclusive(&isa.bits);
return rootRelease_underflow(performDealloc);
}
// Transfer retain count from side table to inline storage.
if (!sideTableLocked) {
ClearExclusive(&isa.bits);
sidetable_lock();
sideTableLocked = true;
// Need to start over to avoid a race against
// the nonpointer -> raw pointer transition.
goto retry;
}
// Try to remove some retain counts from the side table.
size_t borrowed = sidetable_subExtraRC_nolock(RC_HALF);
// To avoid races, has_sidetable_rc must remain set
// even if the side table count is now zero.
if (borrowed > 0) {
// Side table retain count decreased.
// Try to add them to the inline count.
newisa.extra_rc = borrowed - 1; // redo the original decrement too
bool stored = StoreReleaseExclusive(&isa.bits,
oldisa.bits, newisa.bits);
if (!stored) {
// Inline update failed.
// Try it again right now. This prevents livelock on LL/SC
// architectures where the side table access itself may have
// dropped the reservation.
isa_t oldisa2 = LoadExclusive(&isa.bits);
isa_t newisa2 = oldisa2;
if (newisa2.nonpointer) {
uintptr_t overflow;
newisa2.bits =
addc(newisa2.bits, RC_ONE * (borrowed-1), 0, &overflow);
if (!overflow) {
stored = StoreReleaseExclusive(&isa.bits, oldisa2.bits,
newisa2.bits);
}
}
}
if (!stored) {
// Inline update failed.
// Put the retains back in the side table.
sidetable_addExtraRC_nolock(borrowed);
goto retry;
}
// Decrement successful after borrowing from side table.
// This decrement cannot be the deallocating decrement - the side
// table lock and has_sidetable_rc bit ensure that if everyone
// else tried to -release while we worked, the last one would block.
sidetable_unlock();
return false;
}
else {
// Side table is empty after all. Fall-through to the dealloc path.
}
}
// Really deallocate.
if (slowpath(newisa.deallocating)) {
ClearExclusive(&isa.bits);
if (sideTableLocked) sidetable_unlock();
return overrelease_error();
// does not actually return
}
newisa.deallocating = true;
if (!StoreExclusive(&isa.bits, oldisa.bits, newisa.bits)) goto retry;
if (slowpath(sideTableLocked)) sidetable_unlock();
__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);
if (performDealloc) {
((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(dealloc));
}
return true;
}
上述的 objc_object::rootRelease 较为复杂,解释一下大概逻辑为不断的进行引用计数-1,然后去释放对应的sidetable,最后会调用对象的 dealloc 方法
我们来看看 dealloc干了什么
- (void)dealloc {
_objc_rootDealloc(self);
}
inline void
objc_object::rootDealloc()
{
if (isTaggedPointer()) return;
// #define LLVM_LIKELY fastpath 代表大概率会走这个方法
if (fastpath(isa.nonpointer &&
!isa.weakly_referenced &&
!isa.has_assoc &&
!isa.has_cxx_dtor &&
!isa.has_sidetable_rc))
{
assert(!sidetable_present());
free(this);
}
else {
object_dispose((id)this);
}
}
从上面的方法看,大概可以知道如果这个object没有 Associated Objects ,weak对象, c++的destructor(析构函数),引用计数为0 会直接调用 free 函数。(意味着会清除 Associated Objects和weak对象)
那我们再来看 object_dispose函数:
/***********************************************************************
* object_dispose
* fixme
* Locking: none
**********************************************************************/
id
object_dispose(id obj)
{
if (!obj) return nil;
objc_destructInstance(obj);
free(obj);
return nil;
}
***********************************************************************
* objc_destructInstance
* Destroys an instance without freeing memory.
* Calls C++ destructors.
* Calls ARC ivar cleanup.
* Removes associative references.
* Returns `obj`. Does nothing if `obj` is nil.
**********************************************************************/
void *objc_destructInstance(id obj)
{
if (obj) {
// Read all of the flags at once for performance.
bool cxx = obj->hasCxxDtor();
bool assoc = obj->hasAssociatedObjects();
// This order is important.
if (cxx) object_cxxDestruct(obj);
if (assoc) _object_remove_assocations(obj);
obj->clearDeallocating();
}
return obj;
}
inline void
objc_object::clearDeallocating()
{
if (slowpath(!isa.nonpointer)) {
// Slow path for raw pointer isa.
sidetable_clearDeallocating();
}
else if (slowpath(isa.weakly_referenced || isa.has_sidetable_rc)) {
// Slow path for non-pointer isa with weak refs and/or side table data.
clearDeallocating_slow();
}
assert(!sidetable_present());
}
// Slow path of clearDeallocating()
// for objects with nonpointer isa
// that were ever weakly referenced
// or whose retain count ever overflowed to the side table.
NEVER_INLINE void
objc_object::clearDeallocating_slow()
{
ASSERT(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
SideTable& table = SideTables()[this];
table.lock();
// 移除weak
if (isa.weakly_referenced) {
weak_clear_no_lock(&table.weak_table, (id)this);
}
if (isa.has_sidetable_rc) {
table.refcnts.erase(this);
}
table.unlock();
}
当对象被移除的时候,我们不需要手动的去释放Assocation对象和weak对象。
源码参考 objc4-781