首先简单创建项目,调用alloc
并断点:
然后进入汇编模式运行(Debug -> Debug Workflow -> Always Show Disassembly):
我们发现原来调用alloc
时,底层会调用objc_alloc
。
LLVM是构架编译器(compiler)的框架系统,以C++编写而成,用于优化以任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time),对开发者保持开放,并兼容已有脚本。
2000年LLVM开始开发。
2005年Apple雇了Chris Lattner,LLVM也相当于成了Apple的官方支持的编译器。
Apple已经将它用在OpenCL的流水线优化,Xcode已经能使用llvm-gcc编译代码。
- 今天从LLVM源码探究一下究竟:
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 SpecializedResult =
tryGenerateSpecializedMessageSend(CGF, ResultType, Receiver, Args,
Sel, Method, isClassMessage)) {
return RValue::get(SpecializedResult.getValue());
}
//没有对象返回就走msgSend
return GenerateMessageSend(CGF, Return, ResultType, Sel, Receiver, Args, OID,
Method);
}
首先是先调用tryGenerateSpecializedMessageSend
(表示尝试生成特殊消息发送)判断,最终调用函数:
static Optional
tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
llvm::Value *Receiver,
const CallArgList& Args, Selector Sel,
const ObjCMethodDecl *method,
bool isClassMessage) {
...
auto &Runtime = CGM.getLangOpts().ObjCRuntime;
switch (Sel.getMethodFamily()) {
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));
...
}
llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
llvm::Type *resultType) {
return emitObjCValueOperation(*this, value, resultType,
CGM.getObjCEntrypoints().objc_alloc,
"objc_alloc");
}
static llvm::Value *emitObjCValueOperation(CodeGenFunction &CGF,
llvm::Value *value,
llvm::Type *returnType,
llvm::FunctionCallee &fn,
StringRef fnName) {
...
// Call the function.
llvm::CallBase *Inst = CGF.EmitCallOrInvoke(fn, value);//调用函数
// Cast the result back to the original type.
return CGF.Builder.CreateBitCast(Inst, origType);
}
可以发现,调用
alloc
方法会执行到EmitObjCAlloc
函数, 最终执行objc_alloc
。其实这部分是由系统级别的消息处理逻辑,所以NSObject
的初始化是由系统完成的,。
根据文档解释就是,objc_alloc
符号函数比msgSend
快:
/// The ObjC runtime may provide entrypoints that are likely to be faster
/// than an ordinary message send of the appropriate selector.
ObjC运行时可能提供可能更快的入口点
比普通的消息发送合适的选择器。
/// If the runtime does support a required entrypoint, then this method will
/// generate a call and return the resulting value. Otherwise it will return
/// None and the caller can generate a msgSend instead.
如果运行时确实支持所需的入口点,则此方法将支持
生成一个调用并返回结果值。否则它会返回
无,调用者可以生成msgSend代替。
回想之前alloc源码分析文章中的callAlloc
:
// Calls [cls alloc].
id
objc_alloc(Class cls)
{
return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
}
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())) { ... }
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];
}
第一次调用callAlloc
时,还没有返回值又调用了alloc
,难道是循环alloc
吗?当然不是,从LLVM的GeneratePossiblySpecializedMessageSend
函数看,没有返回值时会调用GenerateMessageSend
,回过头来看alloc源码,便是调用alloc
->callAlloc
->class_createInstance
。
(alloc源码和llvm源码分开理解,个人了解还不深,请见谅)