OC對象的本質(zhì)

在main文件里,是這樣實現(xiàn)的

#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

就看到產(chǎn)生了main.cpp文件,打開看下里面的代碼,在里面找到Person的結(jié)構(gòu)體如下

截屏2021-03-25 下午1.19.07.png

可以看到Person類在cpp文件里是一個struct,也就是結(jié)構(gòu)體

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方法調(diào)用的是objc_setProperty,它是所有屬性set方法的封裝函數(shù)
static void _I_Person_setName_(Person * self, SEL _cmd, NSString *name) {
 objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _name), (id)name, 0, 1); }

我在源碼里找到了這個方法的實現(xiàn),實現(xiàn)的本質(zhì)其實就是給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); //實現(xiàn)函數(shù)
}

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了一次,對應(yīng)上面的retain
}
  • 總結(jié):對象的本質(zhì)是結(jié)構(gòu)體

再看main.cpp文件下方,有很多熟悉的結(jié)構(gòu)體可以做很多研究,先寫到這里。

附上關(guān)于clang遇到的問題,以及解決方案

clang -rewrite-objc main.m -o main.cpp 把?標(biāo)?件編譯成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,根據(jù)自己機器的環(huán)境做調(diào)整

xcode安裝的時候順帶安裝了xcrun命令,xcrun命令在clang的基礎(chǔ)上進?了
?些封裝,要更好??些
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 (?機)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容