阅读视图

发现新文章,点击刷新页面。

iOS底层之对象的本质

序言

在iOS日常开发中,我们每天都会创建对象,那么对象的本质是什么呢?对象和类又是如何绑定关联的呢?

clang

OC的底层实现是C++代码,我们可以通过clang中的命令,用C++来还原OC代码。

  • Clang 是一个C语言C++Objective-C语言的轻量级编译器,是由Apple主导编写的
  • Clang 主要用于把源文件编译成底层文件,比如把main.m 文件编译成main.cppmain.o或者可执行文件

clang 常用命令

clang -rewrite-objc main.m -o main.cpp 
//UIKit报错 
clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m
// xcrun命令基于clang基础上进行了封装更好用
//3、模拟器编译 
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 
//4、真机编译 
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp

对象本质探索

在工程中的mian.m文件中定义如下代码,然后编译成main.cpp文件

OC代码

@interface LGPerson : NSObject
{
    NSString * LGName;
}
@property (nonatomic,assign) NSInteger  age;
@end

@implementation LGPerson
@end

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        // Setup code that might create autoreleased objects goes here.
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

cpp代码

image.png

  • cpp文件中,可以看出LGPerson的定义其实是struct,也就是对象的本质是结构体
  • LGPerson_IMP的定义中,有一个struct NSObject_IMPL NSObject_IVARS;,这里的NSObject_IVARS是继承自NSObjectivars,在NSObject_IMPL的定义中,可以看到其ivars只有isa一个成员。

image.png

isa的定义是Class,那我们再找到Class的定义

image.pngClass 类型实际是一个 objc_class类型的结构体指针,这里也能看到我们常用的id其实是objc_object类型的结构体指针,以及SEL函数的一个结构体指针。


struct LGPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *LGName;
    NSInteger _age;
};

static NSInteger _I_LGPerson_age(LGPerson * self, SEL _cmd) { return (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_age)); }

static void _I_LGPerson_setAge_(LGPerson * self, SEL _cmd, NSInteger age) { (*(NSInteger *)((char *)self + OBJC_IVAR_$_LGPerson$_age)) = age; }

可以看到age属性变量有生成getset方法,同时生成了_age变量,而声明的LGName则没有这些方法变化。

总结

  1. 对象的本质其实就是一个结构体,其内存大小成员变量有关,也就是isa+成员+属性,这也验证了在alloc探索中开辟内存大小和ivars有关;
  2. 所有自定义的类都继承自NSObject,而NSObjectobjc_object类型的结构体,其内部有且只有一个成员属性isa

isa指针探究

在前面alloc的底层探究中,我们知道了对象通过obj->initInstanceIsa初始化isa关联类。接下来就看一下isa的结构是怎么样的,以及如何通过isa知道关联类的?

initInstanceIsa方法

inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());
    // 初始化isa
    initIsa(cls, true, hasCxxDtor);
}

initIsa 方法

inline void
objc_object::initIsa(Class cls, bool nonpointer, UNUSED_WITHOUT_INDEXED_ISA_AND_DTOR_BIT bool hasCxxDtor)
{ 
    ASSERT(!isTaggedPointer());
    isa_t newisa(0);   // isa初始化
    if (!nonpointer) { // 非nonpointer指针直接绑定cls
        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;
}

initIsa方法中,如果是非nonpointer类型指针,则直接调setClass方法绑定;否则现有一些位域赋值操作,再调setClass方法绑定。

联合体isa_t

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

private:
    // Accessing the class requires custom ptrauth operations, so
    // force clients to go through setClass/getClass by making this
    // private.
    Class cls;
    
public:

#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD// defined in isa.h
    };
    bool isDeallocating() {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }

    void setDeallocating() {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif

    void setClass(Class cls, objc_object *obj);
    Class getClass(bool authenticated);
    Class getDecodedClass(bool authenticated);
};

在联合体isa_t中,有clsbits两个变量,还有一个ISA_BITFIELD是结构体里面的宏定义变量,是按位域定义的,ISA_BITFIELD的结构如下:

image.png

isa64情况.jpeg 各变量的解释:

  • nonpointer:表示是否对 isa 指针开启指针优化, 0:纯isa指针,1:不止是类对象地址,isa 中包含了类信息、对象的引用计数等;
  • has_assoc:关联对象标志位,0-没有,1-存在;
  • has_cxx_dtor:该对象时候有C++Objc析构器,如果有析构函数,则需要做析构逻辑,没有则可以更快的释放对象;
  • shiftcls:存储类指针的值,开启指针优化的情况下,在ARM64架构中有33位来存储类指针值;
  • magic:用于调试器判断当前对象是真的对象还是未初始化的空间;
  • weakly_referenced:标志对象是否被指向或曾经指向一个ARC的弱变量,没有弱引用对象则可以更快的释放;
  • unused:对象是否释放;
  • has_sidetable_rc:当对象引用计数大于10时,则需要借用该变量存储进位;
  • extra_rc:对象的引用计数值(实际上是引用计数减1),例如:引用计数是10,那么extra_rc为9,如果引用计数大于10,则需要使用到has_sidetable_rc

isa分析总结

  • isa指针分为nonpointer和非nonpointer类型,非nonpointer类型就是一个纯指针,nonpointer类型指针开启了指针优化
  • 指针优化的设计,可以更好的利用isa的内存,程序中有大量的对象,针对isa的优化节省了大量内存。

通过对对象的本质和isa指针的分析,我们已经知道对象是如何与类进行关联的,后面就来探究一下OC中类的结构。

以上是关于OC中对象的本质分析,如有错误或疑问之处,请在评论区留言吧!!!

❌