@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation LGPerson
@end
@interface Teacher : LGPerson
{
NSString *age;
}
@property (nonatomic, strong) NSString *sex;
@property (nonatomic, copy) NSString *other;
@end
@implementation Teacher
@end
複製代碼
如上示例代碼, 使用clang -rewrite-objc main.m
命令, 查看Objective-C
源代碼的c
實現html
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif
extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS; // 繼承自NSObject
NSString *_name;
};
// @property (nonatomic, copy) NSString *name;
/* @end */
// @implementation LGPerson
// 屬性name的get方法
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);
// 屬性name的set方法 copy修飾
static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
#ifndef _REWRITER_typedef_Teacher
#define _REWRITER_typedef_Teacher
typedef struct objc_object Teacher;
typedef struct {} _objc_exc_Teacher;
#endif
extern "C" unsigned long OBJC_IVAR_$_Teacher$_sex;
extern "C" unsigned long OBJC_IVAR_$_Teacher$_other;
struct Teacher_IMPL {
struct LGPerson_IMPL LGPerson_IVARS;// 繼承自LGPerson
// 成員變量
NSString *age;
// 屬性的帶下劃線的成員變量
NSString *_sex;
NSString *_other;
};
// @property (nonatomic, strong) NSString *sex;
// @property (nonatomic, copy) NSString *other;
/* @end */
// @implementation Teacher
static NSString * _I_Teacher_sex(Teacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)); }
// 屬性sex的set方法 strong修飾
static void _I_Teacher_setSex_(Teacher * self, SEL _cmd, NSString *sex) { (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)) = sex; }
static NSString * _I_Teacher_other(Teacher * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_other)); }
// 屬性other的set方法 copy修飾
static void _I_Teacher_setOther_(Teacher * self, SEL _cmd, NSString *other) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Teacher, _other), (id)other, 0, 1); }
// @end
複製代碼
經過查看c
代碼的實現, 發現copy
和strong
修飾的屬性在set
方法不同?objective-c
copy
調用objc_setProperty
, 而strong
並無,答案仍是去LLVM
的找答案?markdown
打開LLVM
的開源工程, 在項目裏搜索objc_setPropery
, app
這裏的映射關係很直觀的能夠看出來:ide
屬性用atomic
&& copy
修飾使用objc_setProperty_atomic_copy
oop
屬性用atomic
&& 沒有copy
修飾使用objc_setProperty_atomic
優化
屬性不是atomic
&& copy
修飾使用objc_setProperty_nonatomic_copy
ui
其它修飾使用 objc_setProperty_atomic_copy
this
在項目中,系統沒有setName
這個方法,調用setName()
會直接sel被hook
掉, 走到底層objc_setPro
這裏去編碼
在objc-781
的源碼裏,對應的方法如上。
copy
修飾的 經過斷點底層是調用的objc_storeStrong
,
strong
修飾的底層調用的也是objc_storeStrong
,源碼中看objc_storeStrong
void objc_storeStrong(id *location, id obj) {
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj); // 對新值retain
*location = obj;
objc_release(prev);// 對舊值的release
}
複製代碼
而後在LLVM
項目中搜索objc_storeStrong
在EmitARCStoreStrongCall
方法裏找到,再去找那裏調用了這個方法, 在GenerateCopyHelperFunction
方法裏
在上邊這個代碼裏, 有一個switch
, 分好多種枚舉BlockCaptureEntityKind
狀況, 針對strong
和weak
有不一樣的處理
/// Represents a type of copy/destroy operation that should be performed for an
/// entity that's captured by a block.
enum class BlockCaptureEntityKind {
CXXRecord, // Copy or destroy
AddressDiscriminatedPointerAuth,
ARCWeak,
ARCStrong,
NonTrivialCStruct,
BlockObject, // Assign or release
None
};
複製代碼
若是是strong
修飾,就會執行EmitARCStoreStrongCall
方法
若是是weak
修飾,就會執行EmitARCCopyWeak
,在底層調用objc_initWeak
/// void \@objc_copyWeak(i8** %dest, i8** %src)
/// Disregards the current value in %dest. Essentially
/// objc_release(objc_initWeak(dest, objc_readWeakRetained(src)))
void CodeGenFunction::EmitARCCopyWeak(Address dst, Address src) {
emitARCCopyOperation(*this, dst, src,
CGM.getObjCEntrypoints().objc_copyWeak,
llvm::Intrinsic::objc_copyWeak);
}
複製代碼
copy
和strong
修飾的屬性在底層編譯是不同的,由編譯器LLVM
對全部屬性做了優化處理, 其中copy
是經過objc_setProperty
賦值, strong
是經過基地址(self)+偏移
來賦值,(也就是指針偏移到成員變量所在的位置)而後還原成strong
類型// 源碼c實現
static void _I_Teacher_setSex_(Teacher * self, SEL _cmd, NSString *sex) { (*(NSString **)((char *)self + OBJC_IVAR_$_Teacher$_sex)) = sex; }
//-----------------------------------
// objc-781源碼
void objc_storeStrong(id *location, id obj) {
id prev = *location;
if (obj == prev) {
return;
}
objc_retain(obj); // 對新值retain
*location = obj;
objc_release(prev);// 對舊值的release
}
複製代碼
上邊倆段代碼作的功能是同樣的,
strong
和copy
在底層調用objc_storeStrong
, 本質都是對新值retain
,對舊值release
weak
在底層是調用的objc_initWeak
編譯器將每一個方法的返回值和參數類型編碼爲一個字符串, 並將其與方法的
selector
關聯在一塊兒, 可使用@encode
編譯器指令來獲取它,給定一個類型,@encode
返回這個類型的字符串編碼,任何可使用sizeof()
操做參數,均可以使用@encode
在Objective-C
的源代碼 c
實現裏,有這樣的一些代碼和符號, 打開經過clang -rewrite-objc main.m
生成的main.cpp
文件,
{{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_LGPerson_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_LGPerson_setName_}}
複製代碼
@16@0:8
, v24@0:8@16
等這些表明什麼?能夠經過Type Encodings - 官方文檔這裏來了解具體的知識
以v24@0:8@16
爲🌰例,
v
表明沒有返回值24
表示總共佔用的字節數@
表示第一個參數id
的類型是一個對象0
表示從0開始,佔8個字節:
表示表明SEL
第二個參數 從8開始,佔8個字節@
第三個@表明set方法到參數類型, 從16開始, 佔8個,一共是24個字節, 就是v24
後邊的總字節數
更多類型能夠對應官網上到這個表。
attribute
方法有編碼類型, 屬性也有編碼類型
{{"sex","T@\"NSString\",&,N,V_sex"},
{"other","T@\"NSString\",C,N,V_other"}}
複製代碼
T
表示類型@
表示變量類型&
表示引用類型N
表示是nonatomic
V
表示variable
變量,也就是下劃線的變量_sex
c
表示copy