object C底层探究 alloc

463 阅读2分钟

alloc 底层调用流程图

未命名文件(3).png

alloc 为什么会调用 objc_alloc ?

查看源码可以看到alloc的调用顺序,oc-->_objc_rootAlloc(self) image.png 但是当我们断点调试的时候,查看汇编可以看到优先调用的是objc_alloc,这是怎么回事呢?

image.png

思路:sel->imp 那么一定是alloc 对应的imp在某个地方被修改了,我们在源文件中搜索objc_alloc

image.png 打开查看代码

image.png 再找到调用fixupMessageRef的地方 image.png

  • fixup是修复的意思,那说明在这之前已经被动过了
  • ? --> dyld --> readimage 到底是什么在dyld之前是alloc最终调用objc_alloc? 答案是clang 我们先下载llvm的源码 github.com/apple/llvm-…

在llvm中查找对alloc的修改

搜索objc_alloc

image.png

shouldUseRuntimeFunctionsForAlloc 在搜索shouldUseRuntimeFunctionsForAlloc

static Optional<llvm::Value *>
tryGenerateSpecializedMessageSend(CodeGenFunction &CGF, QualType ResultType,
                                  llvm::Value *Receiver,
                                  const CallArgList& Args, Selector Sel,
                                  const ObjCMethodDecl *method,
                                  bool isClassMessage) {
  auto &CGM = CGF.CGM;
  if (!CGM.getCodeGenOpts().ObjCConvertMessagesToRuntimeCalls)
    return None;

  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));
        // [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;
/// Allocate the given objc object.
///   call i8* \@objc_alloc(i8* %value)
llvm::Value *CodeGenFunction::EmitObjCAlloc(llvm::Value *value,
                                            llvm::Type *resultType) {
  return emitObjCValueOperation(*this, value, resultType,
                                CGM.getObjCEntrypoints().objc_alloc,
                                "objc_alloc");
}

明显在这里替换成了alloc 调用替换成了objc_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);
}
graph TD
GeneratePossiblySpecializedMessageSend --> tryGenerateSpecializedMessageSend
GeneratePossiblySpecializedMessageSend --> GenerateMessageSend
  1. alloc llvm
  2. alloc-->objc_alloc-标记receiver
  3. objc_alloc-->objc_msgSend
  4. alloc-->_objc_rootAlloc(self) 这也是为什么callAlloc走两次的原因

内存打印小技巧 x/nuf

n 表示要显示的内存单元的个数

u 表示一个地址单元的长度

  • b 表示单字节
  • h 表示双字节
  • w 表示4字节
  • g 表示八字节
  • f 表示显示方式 f表示显示方式,可取如下值
  • x 按十六进制格式显示变量
  • d 按十进制格式显示变量
  • u 按十进制格式显示无符号整形
  • o 按八进制格式显示变量
  • t 按二进制格式显示变量
  • a 按十六进制格式显示变量
  • i 指令地址格式
  • c 按字符格式显示变量
  • f 按浮点格式显示变量