群组
Flutter,iOS,Android,Python等,闲聊工作&技术&问题;
个人主页:https://waitwalker.cn
telegram群链接:https://t.me/joinchat/Ej0o0A1ntlq5ZFIMzzO5Pw
Objective-C是面向对象的语言.面向对象语言有一句话说:”万物皆对象”,这个”对象”从哪里来呢?下面从Runtime源码实现来进一步查看.
一. objc_object声明相关
首先我们先看runtime.h文件,这个文件相当于Runtime对外部提供的接口文件.包含方法,成员变量,分类,属性等结构声明,也包含了objc_class的完整声明.当我们使用Xcode创建一个类的时候一般都是调用的这里.
/**
Runtime对外提供的接口,包含方法,成员变量,分类,属性等结构声明,也包含了objc_class的完整声明
**/
/* Types */
#if !OBJC_TYPES_DEFINED
/// 表示一个类中的方法
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
/// 表示类中的一个成员变量
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;
/// 表示一个分类
/// An opaque type that represents a category.
typedef struct objc_category *Category;
/// 表示一个属性
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;
/// 类的声明结构
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
可以看到objc_class的结构声明中有isa指针,这个指针是Class类型,然后我们顺藤摸瓜,继续查找Class的定义来源.然后找到了objc-private.h文件,进入文件后我们看到结构体objc_class和objc_object的不完整声明如下:
// MARK: - objc_class 和 objc_object 不完整声明
struct objc_class;
struct objc_object;
接着是用typedef 将这两个类型取了两个熟悉的名字:Class指针和id指针:
// MARK: - 将objc_class类型取名为Class指针类型;将objc_object类型取名为id指针类型
typedef struct objc_class *Class;
typedef struct objc_object *id;
往下就是isa的声明union(联合体),isa_t中有两个初始化函数,两个成员变量,cls和bits,还有一个结构体成员.其中在不同的os平台,有不同的声明.
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# define ISA_MAGIC_MASK 0x001f800000000001ULL
# define ISA_MAGIC_VALUE 0x001d800000000001ULL
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# error unknown architecture for packed isa
# endif
// SUPPORT_PACKED_ISA
#endif
#if SUPPORT_INDEXED_ISA
# if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)
// armv7k or arm64_32
# define ISA_INDEX_IS_NPI_BIT 0
# define ISA_INDEX_IS_NPI_MASK 0x00000001
# define ISA_INDEX_MASK 0x0001FFFC
# define ISA_INDEX_SHIFT 2
# define ISA_INDEX_BITS 15
# define ISA_INDEX_COUNT (1 << ISA_INDEX_BITS)
# define ISA_INDEX_MAGIC_MASK 0x001E0001
# define ISA_INDEX_MAGIC_VALUE 0x001C0001
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t indexcls : 15; \
uintptr_t magic : 4; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t deallocating : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 7
# define RC_ONE (1ULL<<25)
# define RC_HALF (1ULL<<6)
nonpointer: 标记此isa是否是tagged pointer优化后的isa.占用1bit.值为1说明是优化后的isa.具体参考链接,tagged pointer是对实例对象的优化,默认是true,并且是isClass的判断,这是我看源码的理解,不对请指正
has_assoc:标记object是否有关联对象,没有,释放更快
has_cxx_dtor:标记是否有西沟函数,没有,释放更快
shiftcls:类对象(Class,meta-Class对象)内存地址信息
magic:标记object是否初始化完成
weakly_refrenced:标记object是否有weak指针指向它
deallocating:标记object是否正在释放
has_sidetable_rc:标记object的extra_rc位数能否存的下object的引用计数,存不下即has_sidetable_rc=1,存在全局的SideTable里面
extra_rc:存储object的引用计数,存不下,存在全局的SideTable里面
下面是object的完整声明:
// MARK: - object的完整声明
struct objc_object {
private:
// 私有成员变量: isa指针
isa_t isa;
// 公有函数
public:
// 通过这个函数获取不支持tagged pointer的类指针
// ISA() assumes this is NOT a tagged pointer object
Class ISA();
// 通过这个函数获取支持tagged pointer的类指针
// getIsa() allows this to be a tagged pointer object
Class getIsa();
// 以下几个函数是isa的初始化函数
// initIsa()用来初始化一个新的对象的isa
// initIsa() should be used to init the isa of new objects only.
// changeIsa 修改一个对象的isa
// If this object already has an isa, use changeIsa() for correctness.
// initInstanceIsa()用来初始化一个实例对象的isa
// initInstanceIsa(): objects with no custom RR/AWZ
// initClassIsa()用来初始化一个类对象的isa
// initClassIsa(): class objects
// initProtocolIsa()用来初始化一个protocol的isa
// initProtocolIsa(): protocol objects
// initIsa()还用来初始化其它类型对象的isa
// initIsa(): other objects
void initIsa(Class cls /*nonpointer=false*/);
void initClassIsa(Class cls /*nonpointer=maybe*/);
void initProtocolIsa(Class cls /*nonpointer=maybe*/);
void initInstanceIsa(Class cls, bool hasCxxDtor);
// changeIsa() should be used to change the isa of existing objects.
// If this is a new object, use initIsa() for performance.
Class changeIsa(Class newCls);
// tagged pointer 相关
bool hasNonpointerIsa();
bool isTaggedPointer();
bool isBasicTaggedPointer();
bool isExtTaggedPointer();
// 是否是Class
bool isClass();
// 关联对象相关
// object may have associated objects?
bool hasAssociatedObjects();
void setHasAssociatedObjects();
// weak指针相关
// object may be weakly referenced?
bool isWeaklyReferenced();
void setWeaklyReferenced_nolock();
// 对象是否有析构函数
// object may have -.cxx_destruct implementation?
bool hasCxxDtor();
// retain 和 release 操作对象的引用计数 声明函数
// Optimized calls to retain/release methods
id retain();
void release();
id autorelease();
// retain 和 release 操作对象的引用计数 实现函数
// Implementations of retain/release methods
id rootRetain();
bool rootRelease();
id rootAutorelease();
bool rootTryRetain();
bool rootReleaseShouldDealloc();
uintptr_t rootRetainCount();
// 释放销毁对象相关
// Implementation of dealloc methods
bool rootIsDeallocating();
void clearDeallocating();
void rootDealloc();
// 私有函数 上面一些公有函数的调用函数
private:
void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
// Slow paths for inline control
id rootAutorelease2();
bool overrelease_error();
#if SUPPORT_NONPOINTER_ISA
// Unified retain count manipulation for nonpointer isa
id rootRetain(bool tryRetain, bool handleOverflow);
bool rootRelease(bool performDealloc, bool handleUnderflow);
id rootRetain_overflow(bool tryRetain);
bool rootRelease_underflow(bool performDealloc);
void clearDeallocating_slow();
// Side table retain count overflow for nonpointer isa
void sidetable_lock();
void sidetable_unlock();
void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
bool sidetable_addExtraRC_nolock(size_t delta_rc);
size_t sidetable_subExtraRC_nolock(size_t delta_rc);
size_t sidetable_getExtraRC_nolock();
#endif
// Side-table-only retain count
bool sidetable_isDeallocating();
void sidetable_clearDeallocating();
bool sidetable_isWeaklyReferenced();
void sidetable_setWeaklyReferenced_nolock();
id sidetable_retain();
id sidetable_retain_slow(SideTable& table);
uintptr_t sidetable_release(bool performDealloc = true);
uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);
bool sidetable_tryRetain();
uintptr_t sidetable_retainCount();
#if DEBUG
bool sidetable_present();
#endif
};
以上就是objc_object的具体声明结构,里面有许多值得深究的问题,也是接下来会继续关注的问题.
二.实例对象的初始化
1.alloc
下面我们来看一个对象的实例化过程:
Class newClass = objc_allocateClassPair(objc_getClass("NSObject"), "newClass", 0);
objc_registerClassPair(newClass);
id newObject = [[newClass alloc]init];
NSLog(@"%s",class_getName([newObject class]));
NSLog(@"Hello, World!");
上面代码时创建一个newClass类,并且用这个新类实例化一个newObject对象.查看一下alloc方法和init方法的调用栈,其中省略了中间过程:
id _objc_rootAlloc(Class cls)
└── static id callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
└── id class_createInstance(Class cls, size_t extraBytes)
└── id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct, size_t *outAllocatedSize)
├── size_t instanceSize(size_t extraBytes)
├── void *calloc(size_t, size_t)
└── inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
其中NSObject.mm中对alloc的实现如下:
+ (id)alloc {
return _objc_rootAlloc(self);
}
这里面调用了一个私有函数,返回一个id类型(objc_object)如下:
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
NSObject.mm对callAlloc()实现:
// Call [cls alloc] or [cls allocWithZone:nil], with appropriate
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
if (slowpath(checkNil && !cls)) return nil;
#if __OBJC2__
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
// No alloc/allocWithZone implementation. Go straight to the allocator.
// fixme store hasCustomAWZ in the non-meta class and
// add it to canAllocFast's summary
if (fastpath(cls->canAllocFast())) {
// No ctors, raw isa, etc. Go straight to the metal.
bool dtor = cls->hasCxxDtor();
id obj = (id)calloc(1, cls->bits.fastInstanceSize());
if (slowpath(!obj)) return callBadAllocHandler(cls);
obj->initInstanceIsa(cls, dtor);
return obj;
}
else {
// Has ctor or raw isa or something. Use the slower path.
id obj = class_createInstance(cls, 0);
if (slowpath(!obj)) return callBadAllocHandler(cls);
return obj;
}
}
#endif
// No shortcuts available.
if (allocWithZone) return [cls allocWithZone:nil];
return [cls alloc];
}
其中有个class_createInstance()函数,这函数中会调用_class_createInstanceFromZone()函数:
static __attribute__((always_inline))
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
bool cxxConstruct = true,
size_t *outAllocatedSize = nil)
{
if (!cls) return nil;
assert(cls->isRealized());
// Read class's info bits all at once for performance
bool hasCxxCtor = cls->hasCxxCtor();
bool hasCxxDtor = cls->hasCxxDtor();
bool fast = cls->canAllocNonpointer();
size_t size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
if (!zone && fast) {
obj = (id)calloc(1, size);
if (!obj) return nil;
obj->initInstanceIsa(cls, hasCxxDtor);
}
else {
if (zone) {
obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
} else {
obj = (id)calloc(1, size);
}
if (!obj) return nil;
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (cxxConstruct && hasCxxCtor) {
obj = _objc_constructOrFree(obj, cls);
}
return obj;
}
这里面有:1)instanceSize()是给对象分配内存空间:
size_t instanceSize(size_t extraBytes) {
size_t size = alignedInstanceSize() + extraBytes;
if (size < 16) size = 16;
return size;
}
uint32_t alignedInstanceSize() {
return word_align(unalignedInstanceSize());
}
uint32_t unalignedInstanceSize() {
assert(isRealized());
return data()->ro->instanceSize;
}
2)initIsa()初始化isa指针
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
inline void
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor)
{
assert(!isTaggedPointer());
// 不是tagged pointer
if (!nonpointer) {
isa.cls = cls;
} else {
assert(!DisableNonpointerIsa);
assert(!cls->instancesRequireRawIsa());
isa_t newisa(0);
#if SUPPORT_INDEXED_ISA
assert(cls->classArrayIndex() > 0);
newisa.bits = ISA_INDEX_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
newisa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.nonpointer is part of ISA_MAGIC_VALUE
newisa.has_cxx_dtor = hasCxxDtor;
newisa.shiftcls = (uintptr_t)cls >> 3;
#endif
// This write must be performed in a single store in some cases
// (for example when realizing a class because other threads
// may simultaneously try to use the class).
// fixme use atomics here to guarantee single-store and to
// guarantee memory order w.r.t. the class index table
// ...but not too atomic because we don't want to hurt instantiation
isa = newisa;
}
}
2.init
init()函数调用栈很简单,只是调用了_objc_rootInit()私有函数,并返回对象本身
- (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;
}
对象的初始化过程可以总结为分配内存空间并且初始化isa_t结构的过程.
编译后源码库
编译后的源码放在Github, 如果对你有帮助,请给一个star吧!
博客地址&相关文章
博客地址: https://waitwalker.github.io/
系列文章:
5. Ivar objc_property_t Protocol解读
参考文献:
https://blog.devtang.com/2014/05/30/understand-tagged-pointer/