美文网首页
iOS底层问题

iOS底层问题

作者: Leevi_w | 来源:发表于2020-03-22 15:55 被阅读0次

    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++] = &prop;
            }
            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<const method_t&,
                                      const method_t&, bool>
      {
          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底层问题

          本文链接:https://www.haomeiwen.com/subject/nrrvyhtx.html