在main文件里,是这样实现的
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation Person
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
}
return 0;
}
把main.m文件编译为cpp文件,命令为:
clang -rewrite-objc main.m -o main.cpp
就看到产生了main.cpp文件,打开看下里面的代码,在里面找到Person的结构体如下
可以看到Person类在cpp文件里是一个struct,也就是结构体
struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
- struct NSObject_IMPL NSObject_IVARS; 是isa指针,下方_I_Person_name是get方法,I_Person_setName 是set方法,而set方法调用的是objc_setProperty,它是所有属性set方法的封装函数
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) {
objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }
我在源码里找到了这个方法的实现,实现的本质其实就是给set新值并且释放旧值
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);//判断属性copy
bool mutableCopy = (shouldCopy == MUTABLE_COPY);//判断属性mutableCopy
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);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
//得到新值newValue
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue); //retain新值newValue
}
if (!atomic) {//非原子型属性修饰, oldValue存放旧值,*slot存放新值
oldValue = *slot;
*slot = newValue;
} else {//原子型操作,使用自旋锁保证set方式的安全,并oldValue存放旧值,*slot存放新值
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);//释放旧值
}
在这里忽然想起被问过这样一个问题,atomic修饰的属性,是在set时用了自旋锁还是set/get都用了。首先上面代码set肯定是用了。这里一眼看到了源码,贴出来。所以atomic修饰的属性在set/get时都用到了自旋锁
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset); //获取属性的值
if (!atomic) return *slot; //非原子型就直接反回了
// Atomic retain release world 下面都是原子型的get
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);//获取属性值,并retain了一次
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);//属性autorelease了一次,对应上面的retain
}
- 总结:对象的本质是结构体
再看main.cpp文件下方,有很多熟悉的结构体可以做很多研究,先写到这里。
附上关于clang遇到的问题,以及解决方案
clang -rewrite-objc main.m -o main.cpp 把⽬标⽂件编译成c++⽂件
UIKit报错问题
clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot /
Applications/Xcode.app/Contents/Developer/Platforms/
iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m
这里可能不是iPhoneSimulator13.0.sdk,根据自己机器的环境做调整
xcode
安装的时候顺带安装了xcrun
命令,xcrun
命令在clang
的基础上进⾏了
⼀些封装,要更好⽤⼀些
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 (⼿机)