一、alloc对象的指针地址和内存
有如下代码:
//alloc后分配了内存,有了指针。
//init所指内存地址一样,init没有对指针进行操作。
HPObject *hp1 = [HPObject alloc];
HPObject *hp2 = [hp1 init];
HPObject *hp3 = [hp1 init];
NSLog(@"%@-%p",hp1,hp1);
NSLog(@"%@-%p",hp2,hp2);
NSLog(@"%@-%p",hp3,hp3);
输出:
<HPObject: 0x600000f84330>-0x600000f84330
<HPObject: 0x600000f84330>-0x600000f84330
<HPObject: 0x600000f84330>-0x600000f84330
说明alloc
后进行了内存分配有了指针,而init
后所指内存地址一致,所以init
没有对指针进行操作。
修改NSLog
内容如下:
NSLog(@"%@-%p &p:%p",hp1,hp1,&hp1);
NSLog(@"%@-%p &p:%p",hp2,hp2,&hp2);
NSLog(@"%@-%p &p:%p",hp3,hp3,&hp3);
输出:
<HPObject: 0x600000e7c2c0>-0x600000e7c2c0 &p:0x7ffeefbf40d8
<HPObject: 0x600000e7c2c0>-0x600000e7c2c0 &p:0x7ffeefbf40d0
<HPObject: 0x600000e7c2c0>-0x600000e7c2c0 &p:0x7ffeefbf40c8
这就说明hp1
、hp2
、hp3
都指向堆空间的一块区域。而3个指针本身是在栈中连续开辟的空间,从高地址->低地址。
那么alloc
是怎么开辟的内存空间呢?
二、底层探索思路
-
断点结合
image.pngStep into instruction
进入调用堆栈找到关键函数:
这里以alloc
为例:
image.png
找到了objc_alloc
关键韩素,然后对它下符号断点:
image.png
找到了最中调用的是libobjc.A.dylib
objc_alloc:`。 -
下断点后通过汇编查看调用流程
image.pngDebug->Debug workflow->Always Show Disassembly
:
可以直接跟进去看调用情况。 -
通过已知符号断点确定未知符号。
image.png
直接alloc
下符号断点跟踪:
三、alloc源码分析
通过上面的分析已经能确定alloc
在objc
框架中,正好苹果开源了这块代码,源码:objc源码地址:Source Browser
最好是自己能编译一份能跑通的源码(也可以直接github上找别人编译好的)。当然也可以根据源码下符号断点跟踪调试。由于objc4-824
目前下载不了,这里以objc4-824.2
为例进行调试。
HPObject
定义如下:
@interface HPObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
3.1 alloc
直接搜索alloc
函数的定义发现在NSObject.mm 2543
,通过断点调试类。
调用alloc
会首先调用objc_alloc
:
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
callAlloc
会走到调用alloc
分支。
+ (id)alloc {
return _objc_rootAlloc(self);
}
alloc
直接调用了_objc_rootAlloc
:
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
-
_objc_rootAlloc
传递参数checkNil
为false
,allocWithZone
为true
直接调用了callAlloc
。 - 在调用
objc_alloc
的时候传递的checkNil
为true
,allocWithZone
为false
。
这里没什么好说的只是方法的一些封装,具体实现要看callAlloc
。
⚠️:明明有
alloc
方法为什么先调用的是objc_alloc
?文章后面会有分析
3.2 callAlloc
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
//表示值为假的可能性更大。即执行else里面语句的机会更大
if (slowpath(checkNil && !cls)) return nil;
//hasCustomAWZ方法判断是否实现自定义的allocWithZone方法,如果没有实现就调用系统默认的allocWithZone方法。
//表示值为真的可能性更大;即执行if里面语句的机会更大
if (fastpath(!cls->ISA()->hasCustomAWZ())) {
return _objc_rootAllocWithZone(cls, nil);
}
#endif
// No shortcuts available.
if (allocWithZone) {
return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
}
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc));
}
slowpath
:表示值为假的可能性更大。即执行else里面语句的机会更大。
fastpath
:表示值为真的可能性更大;即执行if里面语句的机会更大。
OBJC2
:是因为有两个版本。Legacy版本
(早期版本,对应Objective-C 1.0
) 和Modern版本
(现行版本Objective-C 2.0
)。
- 在首次调用的时候会走
alloc
分支进入到alloc
逻辑。 -
hasCustomAWZ
意思是hasCustomAllocWithZone
有没有自定义实现AllocWithZone
。没有实现就走(这里进行了取反)_objc_rootAllocWithZone
,实现了走allocWithZone:
。 - 第二次调用直接走
callAlloc
的其它分支不会调用到alloc
。
⚠️:自己实现一个类的allocWithZone
alloc
分支就每次都被调用了。
3.3 _objc_rootAllocWithZone
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);
}
_objc_rootAllocWithZone
直接调用了_class_createInstanceFromZone
。
3.4 allocWithZone
// Replaced by ObjectAlloc
+ (id)allocWithZone:(struct _NSZone *)zone {
return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
_objc_rootAllocWithZone
直接调用了_objc_rootAllocWithZone
,与上面的3.3
中的逻辑汇合了。
3.5 _class_createInstanceFromZone
最终会调用_class_createInstanceFromZone
进程内存的计算和分配。
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
//判断当前class或者superclass是否有.cxx_construct构造方法的实现
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
//判断当前class或者superclass是否有.cxx_destruct析构方法的实现
bool hasCxxDtor = cls->hasCxxDtor();
//标记类是否支持优化的isa
bool fast = cls->canAllocNonpointer();
size_t size;
//通过内存对齐得到实例大小,extraBytes是由对象所拥有的实例变量决定的。
size = cls->instanceSize(extraBytes);
if (outAllocatedSize) *outAllocatedSize = size;
id obj;
//对象分配空间
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指针
if (!zone && fast) {
obj->initInstanceIsa(cls, hasCxxDtor);
} else {
// Use raw pointer isa on the assumption that they might be
// doing something weird with the zone or RR.
obj->initIsa(cls);
}
if (fastpath(!hasCxxCtor)) {
return obj;
}
construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
return object_cxxConstructFromClass(obj, cls, construct_flags);
}
- 调用
instanceSize
计算空间大小。 - 根据
zone
是否有值调用malloc_zone_calloc
和calloc
进行内存分配。
在calloc
之前分配的obj
是一块脏内存,执行calloc
后才会真正分配内存。执行前后内存地址发生了变化。
image.png
并且可以看到po
只打印出了地址没有打印出类型,这也就说明目前obj
没有绑定到类。需要通过isa
绑定。 - 根据
!zone && fast
分别调用initInstanceIsa
和initIsa
进行isa
实例化。
image.png
执行完initInstanceIsa
后再次打印就有类型了。 - 根据是否有
hasCxxCtor
分别返回obj
和调用object_cxxConstructFromClass
。
3.6 instanceSize 申请内存
在这个函数中调用了instanceSize
计算实例大小:
inline size_t instanceSize(size_t extraBytes) const {
if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
return cache.fastInstanceSize(extraBytes);
}
size_t size = alignedInstanceSize() + extraBytes;
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
}
- 没有缓存的话会调用
alignedInstanceSize
,如果最终的size < 16
会返回16
。 - 有缓存则调用
fastInstanceSize
。 - 正常情况下缓存是在
_read_images
的时候生成的。所以这里一般会走fastInstanceSize
分支。
3.6.1 alignedInstanceSize
#ifdef __LP64__
# define WORD_MASK 7UL
#else
# define WORD_MASK 3UL
uint32_t alignedInstanceSize() const {
return word_align(unalignedInstanceSize());
}
static inline uint32_t word_align(uint32_t x) {
return (x + WORD_MASK) & ~WORD_MASK;
}
uint32_t unalignedInstanceSize() const {
ASSERT(isRealized());
return data()->ro()->instanceSize;
}
-
x
为unalignedInstanceSize
获取。读取的是data()->ro()->instanceSize
实例变量的大小。由ivars
决定。这里为8
,因为默认有个isa
。isa
为Class
,Class
为objc_class
struct *
类型。
@interface NSObject <NSObject> { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wobjc-interface-ivars" Class isa OBJC_ISA_AVAILABILITY; #pragma clang diagnostic pop }
- 字节对齐算法为:
(x + WORD_MASK) & ~WORD_MASK
。WORD_MASK
64
位下为7
,32
位下为3
。
那么对于HPObject
对象计算方法如下:
根据公式可得1:(8 + 7) & ~7
等价于(8 + 7) >>3 << 3
。
根据1可得2:15 & ~7
转换为二进制:0000 1111 & ~0000 0111
=0000 1111 & 1111 1000
计算可得:00001000 = 8
所以alignedInstanceSize
计算就是以8
字节对齐取8
的倍数(算法中是往下取,对于内存分配来讲是往上取)。
那么为什么以8
字节对齐,最后最小分配16
呢?
分配16
是为了做容错处理。以8
字节对齐(选择8
字节是因为8
字节类型是最常用最多的)是以空间换取时间,提高CPU
读取速度。当然这过程中会做一定的优化。
3.6.2 fastInstanceSize
bool hasFastInstanceSize(size_t extra) const
{
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
}
return _flags & FAST_CACHE_ALLOC_MASK;
}
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
-
fastInstanceSize
中会调用align16
,实现如下(16字节对齐):
static inline size_t align16(size_t x) {
return (x + size_t(15)) & ~size_t(15);
}
3.6.3 setInstanceSize
setInstanceSize
代码如下:
void setInstanceSize(uint32_t newSize) {
ASSERT(isRealized());
ASSERT(data()->flags & RW_REALIZING);
auto ro = data()->ro();
if (newSize != ro->instanceSize) {
ASSERT(data()->flags & RW_COPIED_RO);
*const_cast<uint32_t *>(&ro->instanceSize) = newSize;
}
cache.setFastInstanceSize(newSize);
}
在size
变化只会走会更新在缓存中。那么调用setInstanceSize
的地方如下:
-
realizeClassWithoutSwift
:类加载的时候计算。这里包括懒加载和非懒加载。这里会调用方法,根据类的实例变量进行size
计算。这里是在_read_images
的时候调用。 -
class_addIvar
:动态添加属性的时候会重新计算实例大小。 -
objc_initializeClassPair_internal
:动态添加类相关的初始化。
image.png
instanceSize
对于HPObject
而言分配内存大小应该为8(isa) + 8(name)+4(age)= 20
根据内存对齐应该分配24
字节。
而根据调试instanceSize
最终返回了32
。为什么?参考iOS内存对齐
3.7 initInstanceIsa
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
ASSERT(!cls->instancesRequireRawIsa());
ASSERT(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
initInstanceIsa
最终会调用initIsa
。initIsa
最后会对isa
进行绑定:
inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{
ASSERT(!isTaggedPointer());
isa_t newisa(0);
if (!nonpointer) {
newisa.setClass(cls, this);
} else {
ASSERT(!DisableNonpointerIsa);
ASSERT(!cls->instancesRequireRawIsa());
#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
# if ISA_HAS_CXX_DTOR_BIT
newisa.has_cxx_dtor = hasCxxDtor;
# endif
newisa.setClass(cls, this);
#endif
newisa.extra_rc = 1;
}
// 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;
}
-
isa_t
是一个union
。 -
nonpointer
表示是否进行指针优化。不优化直接走setClass
逻辑,优化走else
逻辑。
3.8 alloc调用流程图
alloc调用流程四、llvm优化alloc
为什么调用alloc
最终调用了objc_alloc
?
4.1 objc源码中探索分析
在源码中我们点击alloc
会进入到+ (id)alloc
方法,但是在实际调试中却是先调用的objc_alloc
,系统是怎么做到的呢?
在源码中可以看到上图中的官方注释。所以这里难道是
SEL
和IMP
进行了交换?尝试去源码中找哪里进行了
objc_alloc
替换,最终在void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
方法中找到了如下代码:
fixupMessageRef(refs+i);
fixupMessageRef
实现中有如下核心代码:
可以看到在这个方法中进行了
imp
的重新绑定将alloc
绑定到了objc_alloc
上面。当然retain
、release
等都进行了同样的操作。既然在
_read_images
中出现问题的时候尝试进行fixup
,那么意味着正常情况下在_read_images
之前llvm
的编译阶段就完成了绑定。
4.2 llvm源码探索分析
那么直接在llvm
中搜索objc_alloc
,在ObjCRuntime.h
中发现了如下注释:
/// When this method returns true, Clang will turn non-super message sends of
/// certain selectors into calls to the corresponding entrypoint:
/// alloc => objc_alloc
/// allocWithZone:nil => objc_allocWithZone
这说明方向没有错,最中在CGObjC.cpp
中找到了如下代码:
case OMF_alloc:
if (isClassMessage &&
Runtime.shouldUseRuntimeFunctionsForAlloc() &&
ResultType->isObjCObjectPointerType()) {
// [Foo alloc] -> objc_alloc(Foo) or
// [self alloc] -> objc_alloc(self)
if (Sel.isUnarySelector() && Sel.getNameForSlot(0) == "alloc")
return CGF.EmitObjCAlloc(Receiver, CGF.ConvertType(ResultType));
// [Foo allocWithZone:nil] -> objc_allocWithZone(Foo) or
// [self allocWithZone:nil] -> objc_allocWithZone(self)
if (Sel.isKeywordSelector() && Sel.getNumArgs() == 1 &&
Args.size() == 1 && Args.front().getType()->isPointerType() &&
Sel.getNameForSlot(0) == "allocWithZone") {
const llvm::Value* arg = Args.front().getKnownRValue().getScalarVal();
if (isa<llvm::ConstantPointerNull>(arg))
return CGF.EmitObjCAllocWithZone(Receiver,
CGF.ConvertType(ResultType));
return None;
}
}
break;
可以看出来alloc
最后执行到了objc_alloc
。那么具体的实现就要看CGF.EmitObjCAlloc
方法:
llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
llvm::Type *resultType) {
return emitObjCValueOperation(*this, value, resultType,
CGM.getObjCEntrypoints().objc_alloc,
"objc_alloc");
}
llvm::Value *CodeGenFunction::EmitObjCAllocWithZone(llvm::Value *value,
llvm::Type *resultType) {
return emitObjCValueOperation(*this, value, resultType,
CGM.getObjCEntrypoints().objc_allocWithZone,
"objc_allocWithZone");
}
llvm::Value *CodeGenFunction::EmitObjCAllocInit(llvm::Value *value,
llvm::Type *resultType) {
return emitObjCValueOperation(*this, value, resultType,
CGM.getObjCEntrypoints().objc_alloc_init,
"objc_alloc_init");
}
这里可以看到alloc
以及objc_alloc_init
相关的逻辑。这样就实现了绑定。那么系统是怎么走到OMF_alloc
的逻辑的呢?
通过发送消息走到这块流程:
CodeGen::RValue CGObjCRuntime::GeneratePossiblySpecializedMessageSend(
CodeGenFunction &CGF, ReturnValueSlot Return, QualType ResultType,
Selector Sel, llvm::Value *Receiver, const CallArgList &Args,
const ObjCInterfaceDecl *OID, const ObjCMethodDecl *Method,
bool isClassMessage) {
//尝试发送消息
if (Optional<llvm::Value *> SpecializedResult =
tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,
Sel, Method, isClassMessage)) {
return RValue::get(SpecializedResult.getValue());
}
return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID,
Method);
}
- 苹果对
alloc
等特殊函数做了hook
,会先走底层的标记emitObjCValueOperation
。最终再走到alloc
等函数。 - 第一次会走
tryGenerateSpecializedMessageSend
分支,第二次就走GenerateMessageSend
分支了。- 也就是第一次
alloc
调用了objc_alloc
,第二次alloc
后就没有调用objc_alloc
走了正常的objc_msgSend
:alloc-> objc_alloc -> callAlloc -> alloc -> _objc_rootAlloc -> callAlloc
。这也就是callAlloc
走两次的原因。 - 再创建个对象调用流程就变成了:
alloc -> objc_alloc -> callAlloc
。
- 也就是第一次
五、内存分配优化
HPObject *hpObject = [HPObject alloc];
NSLog(@"%@:",hpObject);
对于hpObject
我们查看它的内存数据如下:
(lldb) x hpObject
0x6000030cc2e0: c8 74 e6 0e 01 00 00 00 00 00 00 00 00 00 00 00 .t..............
0x6000030cc2f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
(lldb) p 0x000000010ee674c8
(long) $4 = 4544951496
可以打印的isa
是4544951496
并不是HPObject
。因为这里要&mask
,在源码中有一个&mask结构。
arm64`定义如下:
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# else
# define ISA_MASK 0x0000000ffffffff8ULL
这样计算后就得到isa
了:
(lldb) po 0x000000010ee674c8 & 0x007ffffffffffff8
HPObject
给HPObjetc
添加属性并赋值,修改逻辑如下:
@interface HPObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) BOOL marry;
@end
调用:
HPObject *hpObject = [HPObject alloc];
hpObject.name = @"HotpotCat";
hpObject.age = 18;
hpObject.height = 180.0;
hpObject.marry = YES;
image.png
这个时候发现
age
和marry
存在了isa
后面存在了一起。那么多增加几个
BOOL
属性呢?
@interface HPObject : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@property (nonatomic, assign) double height;
@property (nonatomic, assign) BOOL marry;
@property (nonatomic, assign) BOOL flag1;
@property (nonatomic, assign) BOOL flag2;
@property (nonatomic, assign) BOOL flag3;
@end
BOOL
属性都设置为YES
查看内存地址:
发现所有BOOL
值都放在了一起。那么再增加一个呢?
可以看到int
类型的age
单独存放了,5
个bool
值放在了一起。这也就是内存分配做的优化。
六、init源码探索
既然alloc
已经完成了内存分配和isa
与类的关联那么init
中做了什么呢?
init
init
源码定义如下:
- (id)init {
return _objc_rootInit(self);
}
_objc_rootInit
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;
}
可以看到init
中调用了_objc_rootInit
,而_objc_rootInit
直接返回obj
没有做任何事情。就是给子类用来重写的,提供接口便于扩展。所以如果没有重写init
方法,那么在创建对象的时候可以不调用init
方法。
有了alloc
底层骚操作的经验后,打个断点调试下:
NSObject *obj = [NSObject alloc];
[obj init];
这里alloc
和init
分开写是为了避免被优化。这时候调用流程和源码看到的相同。
那么修改下调用逻辑:
NSObject *obj = [[NSObject alloc] init];
alloc init
一起调用后会先进入objc_alloc_init
方法。
objc_alloc_init
id
objc_alloc_init(Class cls)
{
return [callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/) init];
}
objc_alloc_init
调用了callAlloc
和init
。
⚠️不同版本的系统下调用逻辑会有不同。
七、new源码探索
既然alloc init
和new
都能创建对象,那么它们之间有什么区别呢?
new
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
与alloc
init
一起调用的不同点是checkNil
传递的是fasle
。
源码调试发现new
调用的是objc_opt_new
// Calls [cls new]
id
objc_opt_new(Class cls)
{
#if __OBJC2__
if (fastpath(cls && !cls->ISA()->hasCustomCore())) {
return [callAlloc(cls, false/*checkNil*/) init];
}
#endif
return ((id(*)(id, SEL))objc_msgSend)(cls, @selector(new));
}
在objc2
下也是callAlloc
和init
。
-
init
方法内部默认没有进行任何操作,只是返回了对象本身。 -
allot init
与new
底层实现一致,都是调用callAlloc
和init
。所以如果自定义了init
方法调用两者效果相同。
objc_alloc_init
和objc_opt_new
的绑定与objc_alloc
的实现相同。同样的实现绑定的还有:const char *AppleObjCTrampolineHandler::g_opt_dispatch_names[] = { "objc_alloc",//alloc "objc_autorelease",//autorelease "objc_release",//release "objc_retain",//retain "objc_alloc_init",// alloc init "objc_allocWithZone",//allocWithZone "objc_opt_class",//class "objc_opt_isKindOfClass",//isKindOfClass "objc_opt_new",//new "objc_opt_respondsToSelector",//respondsToSelector "objc_opt_self",//self };
总结
alloc
调用过程:
-
objc_alloc
-
alloc
底层首先调用的是objc_alloc
。 -
objc_alloc
与alloc
是在llvm
编译阶段进行关联的。苹果会对系统特殊函数做hook
进行标记。
-
-
callAlloc
判断应该初始化的分支。 -
_class_createInstanceFromZone
进行真正的开辟和关联操作:-
instacneSize
计算应该开辟的内存空间。-
alignedInstanceSize
内部进行字节对齐。 -
fastInstanceSize
内部会进行内存对齐。
-
-
calloc
开辟内存空间。 -
initInstanceIsa
关联isa
与创建的对象。
-
- init & new
-
init
方法内部默认没有进行任何操作,只是为了方便扩展。 -
allot init
与new
底层实现一致,都是调用callAlloc
和init
。
-
网友评论