前几章深刻分析了对象的建立过程。本章总结下什么是对象?即对象的本质前端
参考官网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
复制代码
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
,至关于NSObject
的isa
。底层使用这种伪继承
的方式;继承了父类NSObject
的成员isa
。markdown
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);
复制代码
为何在上边的代码中, NSObject
的isa
是Class
类型,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
是继承父类的,直至到继承自NSObject
的isa
在生成的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
在上下层起到承上启下的做用。 连接🔗上层的setter
方法和底层setter
方法,本质就是接口
。缘由是每一个开发者在上层写的
setter
方法,在编译到底层时, 会产生许多相同做用的方法与成员变量。 占用没必要要的空间。增长查找方法的时间。因此苹果采用
适配器模式,底层适配为客户须要的接口
, 对上暴露统一接口,供上层setter
方法使用,对内调用底层setter
方法,相互不受影响,即上层怎么变化, 下层都是不变,反过来,下层的变化也不回影响到上层。达到上下层隔离的目的。