iOS底层问题

1.介绍下runtime的内存模型(isa、对象、类、metaclass、结构体的存储信息等

对象
struct objc_object{
         Class isa OBJC_ISA_AVAILABILITY; 
    };

isa

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits; // uintptr_t本质是 unsigned long  ,在处理器中占用8个字节,正好是64位的二级制
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

类 metaclass

struct objc_class{
    Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
     Class super_class                        OBJC2_UNAVAILABLE;  // 父类
     const char *name                         OBJC2_UNAVAILABLE;  // 类名
     long version                             OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
     long info                                OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
     long instance_size                       OBJC2_UNAVAILABLE;  // 该类的实例变量大小
     struct objc_ivar_list *ivars             OBJC2_UNAVAILABLE;  // 该类的成员变量链表
     struct objc_method_list *methodLists     OBJC2_UNAVAILABLE;  // 方法定义的链表
     struct objc_cache *cache                 OBJC2_UNAVAILABLE;  // 方法缓存
     struct objc_protocol_list *protocols     OBJC2_UNAVAILABLE;  // 协议链表
#endif
}OBJC2_UNAVAILABLE;

metaclass

2.为什么要设计metaclass?

Why is MetaClass in Objective-C? -

大概意思就是类也是一个对象,指针指向父类,最终指向元类,存放类方法

3.class_copyIvarList & class_copyPropertyList区别

首先先看IvarProperty 是什么,class_copyIvarListclass_copyPropertyList区别 在runtime中的实现

  • property在编译期会生成_propertyName的ivar,和相应的get/set属性
  • ivars在编译期确定,但不完全确定,offset属性在运行时会修改
  • 对象的大小是由ivars决定的,当有继承体系时,父类的ivars永远放在子类之前
  • class_ro_t的instanceStart和instanceSize会在运行时调整
  • class_ro_t的ivarLayout和weakIvarLayout存放的是强ivar和弱ivar的存储规则

struct property_t {
    const char *name; //属性名称
    const char *attributes; // 属性
};

struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned 
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the 
    // offset for their benefit.
#endif
    int32_t *offset; // 偏移量,只读写32位
    const char *name; // 变量名称
    const char *type; // 类型
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;  // 编译时期对齐
    uint32_t size; // 变量内存大小

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    } // 对齐方法
};


/***********************************************************************
* class_copyIvarList
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
    const ivar_list_t *ivars;
    Ivar *result = nil;
    unsigned int count = 0;

    if (!cls) {
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    ASSERT(cls->isRealized());
    
    if ((ivars = cls->data()->ro->ivars)  &&  ivars->count) {
        result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
        
        for (auto& ivar : *ivars) {
            if (!ivar.offset) continue;  // anonymous bitfield
            result[count++] = &ivar;
        }
        result[count] = nil;
    }
    
    if (outCount) *outCount = count;
    return result;
}

/***********************************************************************
* class_copyPropertyList. Returns a heap block containing the 
* properties declared in the class, or nil if the class 
* declares no properties. Caller must free the block.
* Does not copy any superclass's properties.
* Locking: read-locks runtimeLock
**********************************************************************/
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
    if (!cls) {
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);
    ASSERT(cls->isRealized());
    
    auto rw = cls->data();

    property_t **result = nil;
    unsigned int count = rw->properties.count();
    if (count > 0) {
        result = (property_t **)malloc((count + 1) * sizeof(property_t *));

        count = 0;
        for (auto& prop : rw->properties) {
            result[count++] = ∝
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    return (objc_property_t *)result;
}

4.class_rw_t 和 class_ro_t 的区别

  • 下面class_rw_tclass_ro_t 结构体
  • class_ro_t编译时创建,class_rw_t运行时创建
  • 在程序初始化class的时候,会把编译器储存在bits里的class_ro_t取出,然后创建class_rw_t,并把ro赋值给rw,ro会成为rw的一个只读的成员变量,最后把rw设置给bits
struct class_rw_t {
  // Be warned that Symbolication knows the layout of this structure.
  uint32_t flags;
  uint32_t version;

  const class_ro_t *ro;

  method_array_t methods;
  property_array_t properties;
  protocol_array_t protocols;

  Class firstSubclass;
  Class nextSiblingClass;

  char *demangledName;

#if SUPPORT_INDEXED_ISA
  uint32_t index;
#endif

  void setFlags(uint32_t set) 
  {
      OSAtomicOr32Barrier(set, &flags);
  }

  void clearFlags(uint32_t clear) 
  {
      OSAtomicXor32Barrier(clear, &flags);
  }

  // set and clear must not overlap
  void changeFlags(uint32_t set, uint32_t clear) 
  {
      assert((set & clear) == 0);

      uint32_t oldf, newf;
      do {
          oldf = flags;
          newf = (oldf | set) & ~clear;
      } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
  }
};

struct class_ro_t {
  uint32_t flags;
  uint32_t instanceStart;
  uint32_t instanceSize;
#ifdef __LP64__
  uint32_t reserved;
#endif

  const uint8_t * ivarLayout;
  
  const char * name;
  method_list_t * baseMethodList;
  protocol_list_t * baseProtocols;
  const ivar_list_t * ivars;

  const uint8_t * weakIvarLayout;
  property_list_t *baseProperties;

  // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
  _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

  _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
      if (flags & RO_HAS_SWIFT_INITIALIZER) {
          return _swiftMetadataInitializer_NEVER_USE[0];
      } else {
          return nil;
      }
  }

  method_list_t *baseMethods() const {
      return baseMethodList;
  }

  class_ro_t *duplicate() const {
      if (flags & RO_HAS_SWIFT_INITIALIZER) {
          size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
          class_ro_t *ro = (class_ro_t *)memdup(this, size);
          ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
          return ro;
      } else {
          size_t size = sizeof(*this);
          class_ro_t *ro = (class_ro_t *)memdup(this, size);
          return ro;
      }
  }
};

5.loadinitialize方法的区别是什么,在继承关系中他们有什么区别?

  • load方法在main方法之前调用,是有系统调用的,只会调用一次
  • initialize在第一次调用类所属的方法时候才会调用
  • load是直接调用的
  • initialize是通过objc_msgSend 调用的
  • 调用顺序都为"父类->子类->类别"
  • load方法不会被覆盖掉都会调用
  • initialize会被子类覆盖

6.IMPSEL, Method 的区别

  • Method的结构体如下
  • Method 包含SELIMP
  • SEL 本质就是字符串
  • IMP 是方法

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

/// A pointer to the function of a method implementation. 
typedef void (*IMP)(void /* id, SEL, ... */ ); 

  struct method_t {
  SEL name;
  const char *types;
  MethodListIMP imp;

  struct SortBySELAddress :
      public std::binary_function
  {
      bool operator() (const method_t& lhs,
                       const method_t& rhs)
      { return lhs.name < rhs.name; }
  };
};

7.category&extention区别,能给NSObject添加extention嘛,结果如何?

  • extention在编译时期就跟原类编译到一起
  • category是在运行时添加待原类中

8.在方法调用的时候,方法查询,动态解析,消息转发之前做了什么?

  • 判断调用的SEL是否要忽略,Mac OS中垃圾回收机制启动,就会忽略retainrelease等方法
  • 判断接收的对象是否为nil
  • 在方法缓存列表中查找,cache_getImp,有的话直接返回IMP
  • 查找当前类的method list,看是否有对应的SEL,有的话就取出对应的Method对象,并从中获得IMP,返回IMP
  • 如果当前类没有,就去父类查找,一样,先查缓存cache list,再查method list,一直到Nsobject
  • 如果没有,就进入动态解析,实际上也是通过objc_msgSend调用NSObject中的resolveInstanceMethodresolveClassMethod两个方法实现动态添加
  • 如果动态解析没有做出响应,就进入动态消息转发阶段。

9.category 如何被加载的,两个cateforyload方法的加载顺序,两个category的同名方法的加载顺序

  • read_images 循环中,遍历所有取出类对应的category数组
  • 然后遍历取出category_t对象
  • 然后执行通过addUnattachedCategoryForClass函数添加到Catefory哈希表中
  • 然后调用remethodizeClass函数从哈希表中取出数组传到attachCategories中执行操作。向对用的类中添加catefory信息
  • 取出Category中方法列表,属性列表,协议列表,通过attachLists函数,添加到类的class_rw_t结构体中
  • 同名方法的加载顺序后 添加的先调用

10.说说消息转发机制的优劣,以及其他语言的消息机制优劣对比
11.runtime的加载过程

  • 程序启动,dyld将应用程序加载到二进制中,完成一些初始化操作
    • dyld 是系统的动态库
  • rumtime向dyld中注入回调函数
  • 通过ImageLoader将所有image加载到内存中
    • ImageLoader 是image的加载器
    • image可以理解为编译后的二进制
  • dyld在image发生改变时,主动调用回调函数
  • runtime收到dyld的函数回调,开始执行map_images,load_images操作并回调load方法
    • 调用_objec_init,初始化_dyld_objc_notify_register(&map_images, load_images, unmap_image)
    • map_images,load_images,unmap_image
      • mapimage执行read_image
        • read_image中完成大量初始化操作
          • 加载所有类到类的gdb_objc_realized_classes表中
          • 对所有类做映射
          • 将所有SEL注册到namedSelectors表中
          • 修复函数指针遗留
          • 将所有Protocol都添加到protocol_map表中
          • 对所有Protocol做重映射
          • 初始化所有非懒加载的类,进行rw,ro操作
          • 遍历已标记的懒加载的类,并做初始化
          • 处理所有Category,包括ClassMeta Class
          • 初始化所有未初始化的类
void _objc_init(void)
{
   static bool initialized = false;
   if (initialized) return;
   initialized = true;
   
   // fixme defer initialization until an objc-using image is found?
   environ_init();
   tls_init();
   static_init();
   lock_init();
   exception_init();

   _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}
  • 调用main函数。开始执行业务代码

weak的实现原理?SideTale的结构?

  • weak属性,会调用initWeak
  • 然后将weak存到弱引用的哈希表中
  • SideTale有一个引用计数的表和一个弱引用计数的表

汇编语言基础

汇编语言入门教程 - 阮一峰的网络日志

你可能感兴趣的:(iOS底层问题)