補(bǔ)充說(shuō)明結(jié)構(gòu)體嵌套
typedef struct person{
char a;
int b;
short c;
}myPerson;
struct p{
int d;
double e;
char f;
myPerson g;
short h;
}per;
結(jié)構(gòu)體嵌套所需開(kāi)辟的內(nèi)存空間是結(jié)構(gòu)體內(nèi)最大長(zhǎng)度數(shù)據(jù)成員(非結(jié)構(gòu)體)
所占大小的整數(shù)倍
像啼。
如圖所示:
-
myPerson
本身作為結(jié)構(gòu)體付翁,遵循內(nèi)存對(duì)齊原則
蓝厌,故而所占12個(gè)字節(jié)
- 而
per
作為嵌套結(jié)構(gòu)體秦爆,加入myPerson
結(jié)構(gòu)體時(shí)應(yīng)該是myPerson
內(nèi)最大成員變量
的整數(shù)倍開(kāi)始撞牢,由代碼可知,myPerson
中最大為int類型
轻猖,占4個(gè)字節(jié)
帆吻,而myPerson
的起始位置為17
,根據(jù)min(17咙边,4)
得出起始位置應(yīng)該為20
,并且其自身占12個(gè)字節(jié)
故而所占位數(shù)為(20-31)
- 根據(jù)
內(nèi)存規(guī)則
次员,所占內(nèi)存必須為最大成員
所占的整數(shù)倍
败许,故而為8的整數(shù)倍
,最小即為min(33,8)= 40
- 總結(jié):結(jié)構(gòu)體嵌套時(shí)淑蔚,
本身
和嵌套的結(jié)構(gòu)體
都要滿足內(nèi)存對(duì)齊規(guī)則
市殷,不足的自動(dòng)補(bǔ)齊
類的結(jié)構(gòu)中class_rw_t
與class_ro_t
的區(qū)別
class_ro_t
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;
...
};
-
class_ro_t
存儲(chǔ)了當(dāng)前類在編譯期
就已經(jīng)確定的屬性
、方法
以及遵循的協(xié)議
刹衫,里面是沒(méi)有分類的方法的醋寝。那些運(yùn)行時(shí)
添加的方法將會(huì)存儲(chǔ)在運(yùn)行時(shí)生成的class_rw_t
中。 -
ro
即表示read only
带迟,是無(wú)法進(jìn)行修改
的音羞。
class_rw_t
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
...
}
-
ObjC
類中的屬性、方法還有遵循的協(xié)議
等信息都保存在class_rw_t
中:
class_rw_t
生成時(shí)機(jī)
class_rw_t
生成在運(yùn)行時(shí)仓犬,在編譯期間嗅绰,class_ro_t
結(jié)構(gòu)體就已經(jīng)確定,objc_class
中的bits
的data
部分存放著該結(jié)構(gòu)體的地址
搀继。在runtime
運(yùn)行之后窘面,具體說(shuō)來(lái)是在運(yùn)行runtime
的realizeClass
方法時(shí),會(huì)生成class_rw_t
結(jié)構(gòu)體叽躯,該結(jié)構(gòu)體包含了class_ro_t
财边,并且更新data
部分,換成class_rw_t
結(jié)構(gòu)體的地址点骑。
類的realizeClass
運(yùn)行之前如下圖所示:
類的
realizeClass
運(yùn)行之后如下圖所示:由此可見(jiàn)酣难,
class_rw_t
與class_ro_t
中的成員變量有一些是相同的谍夭,區(qū)別在于:class_ro_t
存放的是編譯期間就確定的;而class_rw_t
是在runtime
時(shí)才確定鲸鹦,它會(huì)先將class_ro_t
的內(nèi)容拷貝
過(guò)去慧库,然后再將當(dāng)前類的分類
的這些屬性、方法
等拷貝到其中馋嗜。所以可以說(shuō)class_rw_t
是class_ro_t
的超集齐板,當(dāng)然實(shí)際訪問(wèn)類的方法、屬性
等也都是訪問(wèn)的class_rw_t
中的內(nèi)容
之前我們?cè)诖蛴☆惖慕Y(jié)構(gòu)時(shí)葛菇,只能讀取到其中的屬性以及實(shí)例方法甘磨,但是其中的成員變量
與類方法
均沒(méi)有讀取到,接下來(lái)我們就分析一下:
@interface LGPerson : NSObject
{
NSString *hobby;
NSObject *objc;
id age;
}
@property (nonatomic, copy) NSString *nickName;
@property (nonatomic, strong) NSString *name;
@end
成員變量眯停、實(shí)例變量济舆、屬性之間的關(guān)系
- 如上面代碼所示,
{}
中的都是成員變量
莺债,而其中的objc
就是實(shí)例變量
滋觉,(id是OC特有的類,本質(zhì)上等于(void *))
齐邦,所以age
也是實(shí)例變量
-
實(shí)例變量
就是Class
類通過(guò)實(shí)例化
出來(lái)的對(duì)象椎侠,是一種特殊的成員變量
,實(shí)例變量+基本數(shù)據(jù)類型變量=成員變量
-
成員變量
一般用于類內(nèi)部
措拇,不會(huì)生成setter我纪、getter
方法,外界無(wú)法獲取到
-
-
屬性變量
就是自動(dòng)生成setter丐吓、getter
方法浅悉,其他對(duì)象可以進(jìn)行訪問(wèn)
copy與strong的實(shí)現(xiàn)
static NSString * _I_LGPerson_nickName(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_nickName)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
static void _I_LGPerson_setNickName_(LGPerson * self, SEL _cmd, NSString *nickName) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _nickName), (id)nickName, 0, 1); }
static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)) = name; }
首先通過(guò)clang
生成的main.cpp
文件中可以看出通過(guò)copy
修飾的屬性會(huì)含有objc_setProperty
,而通過(guò)strong
修飾的就沒(méi)有
copy
是在編譯時(shí)
就已經(jīng)進(jìn)行處理調(diào)用objc_setProperty
方法
//通過(guò)調(diào)用GetOptimizedPropertySetFunction()方法去判斷對(duì)應(yīng)的屬性券犁,是否為atomic及是否為copy
llvm::FunctionType *FTy =
Types.GetFunctionType( Types.arrangeBuiltinFunctionDeclaration(Ctx.VoidTy, Params));
const char *name;
if (atomic && copy)
name = "objc_setProperty_atomic_copy";
else if (atomic && !copy)
name = "objc_setProperty_atomic";
else if (!atomic && copy)
name = "objc_setProperty_nonatomic_copy";
else
name = "objc_setProperty_nonatomic";
//根據(jù)對(duì)應(yīng)的屬性修飾查找
//通過(guò)runtime返回對(duì)應(yīng)的sel進(jìn)行value賦值
return CGM.CreateRuntimeFunction(FTy, name);
strong
方法通過(guò)引用計(jì)數(shù)retain
進(jìn)行查看
void
objc_storeStrong(id *location, id obj)
{
id prev = *location;
if (obj == prev) {
return;
}
// 新值賦值
objc_retain(obj);
*location = obj;
// 舊值釋放
objc_release(prev);
}
通過(guò)llvm
中進(jìn)行查找
case BlockCaptureEntityKind::ARCStrong: {
llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src");
// At -O0, store null into the destination field (so that the
// storeStrong doesn't over-release) and then call storeStrong.
// This is a workaround to not having an initStrong call.
if (CGM.getCodeGenOpts().OptimizationLevel == 0) {
auto *ty = cast<llvm::PointerType>(srcValue->getType());
llvm::Value *null = llvm::ConstantPointerNull::get(ty);
Builder.CreateStore(null, dstField);
EmitARCStoreStrongCall(dstField, srcValue, true);
} else {
EmitARCRetainNonBlock(srcValue);
if (!needsEHCleanup(captureType.isDestructedType()))
cast<llvm::Instruction>(dstField.getPointer())->eraseFromParent();
}
break;
}
llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(Address addr,
llvm::Value *value,
bool ignored) {
assert(addr.getElementType() == value->getType());
llvm::Function *&fn = CGM.getObjCEntrypoints().objc_storeStrong;
if (!fn) {
fn = CGM.getIntrinsic(llvm::Intrinsic::objc_storeStrong);
setARCRuntimeFunctionLinkage(CGM, fn);
}
llvm::Value *args[] = {
Builder.CreateBitCast(addr.getPointer(), Int8PtrPtrTy),
Builder.CreateBitCast(value, Int8PtrTy)
};
EmitNounwindRuntimeCall(fn, args);
if (ignored) return nullptr;
return value;
}
當(dāng)case BlockCaptureEntityKind:為ARCStrong
時(shí)术健,->EmitARCStoreStrongCall()
->objc_stroeStrong
方法,進(jìn)而可以找到上述底層編碼
獲取成員變量及類方法所在位置
首先讀取讀取
LGperson
類去獲取對(duì)應(yīng)的bits
根據(jù)上面
class_rw_t
與class_ro_t
的區(qū)別得出族操,class_rw_t
本身是包含ro
的苛坚,所以我們通過(guò)上圖去讀取對(duì)應(yīng)的ro
,得出其中除了基礎(chǔ)類型baseProperties
外還有ivars
色难,通過(guò)讀取ivars
可以看出成員變量
是存在中的泼舱,因此得出成員變量
本身是不能被外界所讀取
。我們通過(guò)查看
LGPerson
的isa
去查看對(duì)應(yīng)的元類
信息枷莉,看出其中是包含LGPerson
中的類方法 :say666
娇昙,故而我們得出類方法是存在于其對(duì)應(yīng)的元類
中。
方法簽名
- 每一個(gè)方法都會(huì)包含
sel(方法編號(hào))
與imp(函數(shù)指針笤妙,指向函數(shù)方法的實(shí)現(xiàn))
- 第一個(gè)參數(shù):返回值
v(void) @(id)
- 第二個(gè)參數(shù):開(kāi)辟的內(nèi)存
總字節(jié)數(shù)
- 第三個(gè)參數(shù):傳入的參數(shù)冒掌,位置從0開(kāi)始噪裕,占用(0-7)
- 第四個(gè)參數(shù):
:代表sel(方法編號(hào))
,位置從8開(kāi)始占用(8-15)
下面我們打印了一部分股毫,具體的對(duì)應(yīng)關(guān)系可以去蘋(píng)果官網(wǎng)Type Encoding 查看
面試題解析
BOOL re1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL re2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL re3 = [(id)[LGPerson class] isKindOfClass:[LGPerson class]];
BOOL re4 = [(id)[LGPerson class] isMemberOfClass:[LGPerson class]];
NSLog(@" re1 :%hhd\n re2 :%hhd\n re3 :%hhd\n re4 :%hhd\n",re1,re2,re3,re4);
BOOL re5 = [(id)[NSObject alloc] isKindOfClass:[NSObject class]];
BOOL re6 = [(id)[NSObject alloc] isMemberOfClass:[NSObject class]];
BOOL re7 = [(id)[LGPerson alloc] isKindOfClass:[LGPerson class]];
BOOL re8 = [(id)[LGPerson alloc] isMemberOfClass:[LGPerson class]];
NSLog(@" re5 :%hhd\n re6 :%hhd\n re7 :%hhd\n re8 :%hhd\n",re5,re6,re7,re8);
首先我們通過(guò)方法查找到對(duì)應(yīng)的類方法
和實(shí)例方法
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = self->ISA(); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}
+ (BOOL)isMemberOfClass:(Class)cls {
return self->ISA() == cls;
}
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}
+ (Class)class {
return self;
}
解析
- 首先
[NSObject class]
為類膳音,需要調(diào)用對(duì)應(yīng)的類方法,根元類的superclass
是指向NSObject
铃诬,故而兩者相等- 而
[LGPerson class]
為LGPerson
祭陷,里面循環(huán)查找superclass
,沒(méi)有與LGPerson
相等的類趣席,故而返回NO
- 而
- 根據(jù)
isMemberOfClass
方法源碼可以得出兵志,self->ISA()
指向的是根元類
,而NSObject并不等于根元類
宣肚,所以返回NO
- 根據(jù)
isKindOfClass
實(shí)例方法可以得出想罕,tcls == [self class]
,第一次
循環(huán)時(shí)直接相等霉涨,返回YES
- 根據(jù)
isMemberOfClass
實(shí)例方法得出按价,根據(jù)[self class]
返回的是self
,故而傳入的與返回的相等 - 注:(如果
元類笙瑟、父類俘枫、isa
之間的走位
不懂可以參考下面月月大神的流程圖)
面試題類方法與實(shí)例方法存在位置
void lgInstanceMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));
Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));
LGLog(@"%s - %p-%p-%p-%p",__func__,method1,method2,method3,method4);
<!--lgInstanceMethod_classToMetaclass - 0x10000-->31b0-0x0-0x0-0x100003148
}
void lgClassMethod_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));
Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));
LGLog(@"%s-%p-%p-%p-%p",__func__,method1,method2,method3,method4);
}
void lgIMP_classToMetaclass(Class pClass){
const char *className = class_getName(pClass);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
IMP imp1 = class_getMethodImplementation(pClass, @selector(sayHello));
IMP imp2 = class_getMethodImplementation(metaClass, @selector(sayHello));
IMP imp3 = class_getMethodImplementation(pClass, @selector(sayHappy));
IMP imp4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
NSLog(@"%p-%p-%p-%p",imp1,imp2,imp3,imp4);
}
解析
* 1.class_getInstanceMethod
是否存在對(duì)應(yīng)的實(shí)例方法
,根據(jù)打印結(jié)果可以看出逮走,method1
與method4
有值,而method2今阳、method3
沒(méi)有值师溅,故而得出,實(shí)例方法存在對(duì)應(yīng)的類
中盾舌,而類方法是存在對(duì)應(yīng)的元類
中墓臭。
* 2.class_getClassMethod
是否存在對(duì)應(yīng)的類方法
,根據(jù)打印結(jié)果看出妖谴,method1窿锉、method2
沒(méi)有值,method3膝舅、method4
有值嗡载,故而得出類方法可以在自身類與對(duì)應(yīng)的元類中
找到
* 3.class_getMethodImplementation
查看實(shí)例方法與類方法
對(duì)應(yīng)的imp
,根據(jù)結(jié)果可以看出imp2,imp3
是一致的仍稀,根據(jù)下面源碼得出洼滚,如果沒(méi)有對(duì)應(yīng)的imp即sel方法
則會(huì)固定返回_objc_msgForward
,否則返回imp
技潘。
IMP class_getMethodImplementation(Class cls, SEL sel)
{
IMP imp;
if (!cls || !sel) return nil;
imp = lookUpImpOrNil(nil, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER);
// Translate forwarding function to C-callable external version
if (!imp) {
return _objc_msgForward;
}
return imp;
}