Objective-C面試資料整理

Objective-C的本質(zhì)

Objective-C -> C/C++  -> 匯編語言 ->機器語言

Objective-C 簡單轉(zhuǎn)換 C\C++代碼(代碼比較多)

clang -rewrite-objc main.m -o main.cpp

轉(zhuǎn)換成對應平臺下cpp文件
模擬器(i386, x86-64) 32bit(armv7) 64bit(arm64)

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

Objective-C的面向?qū)ο蠖际腔贑\C++實現(xiàn)的,本質(zhì)是結(jié)構(gòu)體.

@interface NSObject <NSObject> {
    Class isa;
}

struct NSObject_IMPL {
    Class isa;
};

typedef struct objc_class *Class;

至少需要多少內(nèi)存(計算結(jié)構(gòu)體內(nèi)存大小)

class_getInstanceSize([NSObject class]) // 8

實際分配內(nèi)存大小(操作系統(tǒng)內(nèi)存對齊)

malloc_size((__bridge const void *)([[NSObject alloc] init])) // 16

指針變量obj占用多少內(nèi)存空間

NSObject *obj = [[NSObject alloc] init];
sizeof(obj); //8

Objective-C的分類

instance對象(實例對象)

obj是NSObject的instance對象(實例對象)
NSObject *obj = [[NSObject alloc] init];

instance對象在內(nèi)存中存儲信息主要包括
 isa指針
 其他成員變量
 ......

class對象(類對象)

objClass1,objClass2,objClass3是同一個對象,每個類有且只有一個類對象
Class objClass1 = [obj class];
Class objClass2 = [NSObject class];
Class objClass3 = object_getClass(obj);

class對象在內(nèi)存中存儲信息主要包括
isa指針
superClass指針
類的屬性(@property),類的對象方法(instance method)
類的協(xié)議(protocol),類的成員變量(ivar)
......

meta-class對象(元類對象)

objMetaClass是NSObject的元類對象,每個類有且只有一個元類對象
Class objMetaClass = object_getClass([NSObject class]);

是否是元類對象
BOOL isMeta = class_isMetaClass(objMetaClass);

元類對象是特殊的類對象

meta-class對象在內(nèi)存中存儲信息主要包括
isa指針
superClass指針
類的類方法(class method)
......

isa指針和superClass指針

instance對象 isa -> class對象 -> meta-class對象 -> 基類的meta-class對象
class的superClass -> 父類的class(若是空,superClass是nil)

meta-class的superClass -> 父類的meta-class
基類的meta-class的superClass -> 基類的class

instance調(diào)用對象方法路徑: isa找到class,若不存在,通過superClass找父類
class調(diào)用類方法路徑:isa找meta-class,若不存在,通過superClass找父類

isa和superClass.png

isa指針

從64位開始優(yōu)化,需要&ISA_MASK
instance的 isa & ISA_MASK 得到 class的isa
class的 isa & ISA_MASK 得到 meta-class 的isa

objc4源碼查看 https://opensource.apple.com/tarballs/objc4/

typedef struct objc_class *Class;
struct objc_class {
    Class isa;
    Class superClass;
    cache_t cache;           //方法緩存
    class_data_bits_t bits;  //用于獲取具體的類信息
    ......
};

bits & FAST_DATA_MASK 得到 class_rw_t

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
    ......
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
    method_list_t *baseMethods() const {
        return baseMethodList;
    }
};


KVO的全稱是Key-Value Observing,俗稱“鍵值監(jiān)聽”,可以用于監(jiān)聽某個對象屬性值的改變
KVO代碼層面使用

//添加監(jiān)聽屬性
- (void)addKVOTest {
    self.kvo = [[KVOTest alloc] init];
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
    [self.kvo addObserver:self forKeyPath:@"age" options:options context:@"123"];
}

//合適時機銷毀
- (void)dealloc {
    [self.kvo removeObserver:self forKeyPath:@"age"];
}
//監(jiān)聽屬性的變化
- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary<NSKeyValueChangeKey,id> *)change
                       context:(void *)context {
    
    NSLog(@"監(jiān)聽到%@的%@屬性值改變了 - %@ - %@", object, keyPath, change, context);
}

KVO底層原理:
使用KVO監(jiān)聽后,runtime會動態(tài)創(chuàng)建一個 NSKVONotifying_監(jiān)聽類,是監(jiān)聽類的子類
調(diào)用方法順序
willChangeValueForKey:
setAge: ( Foundation下_NSSetIntValueAndNotify() )
observerValueForKey
didChangeValueForKey:

KVC的全稱是Key-Value Coding,俗稱“鍵值編碼”,可以通過一個key來訪問某個屬性

  • (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
  • (void)setValue:(id)value forKey:(NSString *)key;
  • (id)valueForKeyPath:(NSString *)keyPath;
  • (id)valueForKey:(NSString *)key;

setValueForKey的流程
1、按照setKey、_setKey順序查找方法, 找到了直接調(diào)用方法
2、如果找不到,查看accessInstanceVariablesDirectly(能否直接訪問成員變量)的返回值,
NO,直接報錯 setValue:forUndefinedKey:,
YES,通過_key, _isKey, key, isKey順序查找成員變量,找到了直接賦值,都找不到直接報錯 setValue:forUndefinedKey:

valueForKey的流程
1、按照getKey、Key、isKey、_key的順序查找方法,找到直接調(diào)用
2、如果找不到,查看accessInstanceVariablesDirectly(能否直接訪問成員變量)的返回值,
NO,直接報錯 setValue:forUndefinedKey:,
YES,通過_key, _isKey, key, isKey順序查找成員變量,找到了直接取值,都找不到直接報錯 setValue:forUndefinedKey:

Category

分類的底層結(jié)構(gòu)
終端輸入

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc NSObject+FenLeiTest.m

struct _category_t {
    const char *name;
    struct _class_t *cls;
    const struct _method_list_t *instance_methods;
    const struct _method_list_t *class_methods;
    const struct _protocol_list_t *protocols;
    const struct _prop_list_t *properties;
};

1、通過Runtime加載某個類的所有Category數(shù)據(jù)
2、把所有Category的方法、屬性、協(xié)議數(shù)據(jù),合并到一個大數(shù)組中
后面參與編譯的Category數(shù)據(jù),會在數(shù)組的前面
3、將合并后的分類數(shù)據(jù)(方法、屬性、協(xié)議),插入到類原來數(shù)據(jù)的前面

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

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