手撕iOS底层07 -- What is object?

手撕iOS底层07 -- What is object?

前几章深刻分析了对象的建立过程。本章总结下什么是对象?即对象的本质前端


0x00 -- Clang介绍

参考官网ios

Clang就是LLVM项目中的一个c家族(C, C++, Objective C/C++, OpenCL, CUDA, and RenderScript)前端编译器,与GCC同样,有编译器的功能。 固然也有其它的特性, 能够去参考官网了解。c++

主要用于底层编译,将一些oc文件输出成cpp文件,main.m ---> main.cpp;为了更好的观底层的一些结构与实现的逻辑, 方便开发者了解底层。objective-c


//一、将 main.m 编译成 main.cpp
clang -rewrite-objc main.m -o main.cpp

//二、将 ViewController.m 编译成  ViewController.cpp
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot / /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk ViewController.m

//如下两种方式是经过指定架构模式的命令行,使用xcode工具 xcrun
//三、模拟器文件编译
- xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp 

//四、真机文件编译
- xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main- arm64.cpp 
复制代码

0x01 -- OC对象的本质

main.m文件shell

@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end
复制代码

main.cpp文件,经过clang -rewrite-objc main.m -o main.cpp将编译出来的main.cpp文件打开后,xcode

//NSObject的定义
@interface NSObject <NSObject> {
    Class isa  OBJC_ISA_AVAILABILITY;
}
// NSObject底层编译
struct NSObject_IMPL {
	Class isa;
};

struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS; // isa
	NSString *_name;
};
复制代码

LGPerson类在底层编译成一个结构体LGPerson_IMPL,第一个成员变量struct NSObject_IMPL NSObject_IVARS,至关于NSObjectisa。底层使用这种伪继承的方式;继承了父类NSObject的成员isamarkdown

0x02 -- NSObject的isa为何是class

在前边的分析中initInstanceIsa中, 初始化isa的类型是一个union isa_t ,即一个联合体的类型。架构

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

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h 这里是一些位域信息
    };
#endif
};

isa = isa_t((uintptr_t)cls);
复制代码

为何在上边的代码中, NSObjectisaClass类型,app

  • 第一是对外展现为Class类型 ,从而对外反馈的是类信息。为了让开发人员更加明确。
  • 第二是在 Class ISA();的时候,返回isa的时候,作了Class类型强转
inline Class objc_object::ISA() {
    ASSERT(!isTaggedPointer()); 
#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK); // 强转成为Class
#endif
}
复制代码

总结

  • OC对象本质就是结构体
  • 每一个一个自定义的类中的isa是继承父类的,直至到继承自NSObjectisa

0x03 -- objc_setProperty探索

在生成的main.cpp文件中, 在Objective-C代中,把属性编译成这样的iphone

// @property (nonatomic, copy) NSString *nickName;
// ----------------------------
struct LGPerson_IMPL {
	struct NSObject_IMPL NSObject_IVARS;
	NSString *_name; // 下划线成员变量
};
// @property (nonatomic, copy) NSString *name;
/* @end */


// @implementation LGPerson
// getter方法
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// setter方法
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
复制代码

众所周知, OC中的属性,会生成一对getter+setter方法,和一个带下划线的成员变量

setter方法中,调用了一个objc_setProperty 的方法,在objc-781源码中,搜索这个方法的实现

void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
    bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
    bool mutableCopy = (shouldCopy == MUTABLE_COPY);
    reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
    if (offset == 0) {
        object_setClass(self, newValue); // 设置isa指向
        return;
    }

    id oldValue;
    id *slot = (id*) ((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue); // 新值retain
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }

    objc_release(oldValue);// 旧值release
}
复制代码

在这个方法里真正的去调用了底层RuntimeAPIreallySetProperty的方法。在reallySetProperty主要作了oc属性setter方法作的事情, 持有新值retain,释放旧值release


总结objc_setProperty

objc_setProperty在上下层起到承上启下的做用。 连接🔗上层的setter方法和底层setter方法,本质就是接口

缘由是每一个开发者在上层写的setter方法,在编译到底层时, 会产生许多相同做用的方法与成员变量。 占用没必要要的空间。增长查找方法的时间。

因此苹果采用适配器模式,底层适配为客户须要的接口, 对上暴露统一接口,供上层setter方法使用,对内调用底层setter方法,相互不受影响,即上层怎么变化, 下层都是不变,反过来,下层的变化也不回影响到上层。达到上下层隔离的目的。


欢迎大佬留言指正😄,码字不易,以为好给个赞👍 有任何表达或者理解失误请留言交流;共同进步;

相关文章
相关标签/搜索