一官觅、Category的實(shí)現(xiàn)原理
1、Category編譯之后的底層結(jié)構(gòu)是struct category_t,里面存儲(chǔ)著分類的對(duì)象方法,類方法,屬性,協(xié)議信息.
2、在程序運(yùn)行的時(shí)候,runtime會(huì)將Category的數(shù)據(jù),合并到類信息中(類對(duì)象,元類信息中)
Category結(jié)構(gòu)體的定義:
struct category_t {
constchar*name;//類的名字(name)
classref_t cls;//類(cls)
struct method_list_t *instanceMethods; //category中所有給類添加的實(shí)例方法的列表(instanceMethods)
structmethod_list_t *classMethods;//category中所有添加的類方法的列表(classMethods)
structprotocol_list_t *protocols; //category實(shí)現(xiàn)的所有協(xié)議的列表(protocols)
structproperty_list_t *instanceProperties;//category中添加的所有屬性(instanceProperties)
};
從category的定義也可以看出category可以添加實(shí)例方法,類方法席里;可以遵守協(xié)議,添加屬性阵具;但無(wú)法添加實(shí)例變量碍遍。
添加方法列表的時(shí)候是后添加的在新形成的列表前部,這也是為什么在有多個(gè)category中有同名方法時(shí)阳液,后編譯的在調(diào)用時(shí)會(huì)“覆蓋”前面已編譯的方法怕敬。其實(shí)方法本身并沒(méi)有被覆蓋,只是調(diào)用的時(shí)候是從上而下查找方法列表帘皿。
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
newArray->count = newCount;
array()->count = newCount;
將以前的數(shù)組和新的分類數(shù)組合并
for (int i = oldCount - 1; i >= 0; I--)
newArray->lists[i + addedCount] = array()->lists[I]; 遍歷把以前的信息放入到新數(shù)組最后的位置
for (unsigned i = 0; i < addedCount; i++)通過(guò)遍歷把新的信息按照遞增放入到新數(shù)組位置
newArray->lists[i] = addedLists[I];
free(array());
setArray(newArray);
validate();
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
validate();
}
else {
// 1 list -> many lists
Ptr<List> oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
for (unsigned i = 0; i < addedCount; I++)
array()->lists[i] = addedLists[I];
validate();
}
}
說(shuō)到Category方法的調(diào)用可以看看CompileSources文件放的位置
這個(gè)時(shí)候日志打印對(duì)象方法 test方法LCPersonTest1
之所以是LCPersonTest1調(diào)用是因?yàn)樗鼤?huì)把后放入的位置放到分類數(shù)組的首位然后是Test2而我們的LCPerson是放在數(shù)組的最后位置 按照這個(gè)方
式可參考源碼
void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
const struct mach_header * const mhdrs[])
{
static bool firstTime = YES;
header_info *hList[mhCount];
uint32_t hCount;
size_t selrefCount = 0;
// Perform first-time initialization if necessary.
// This function is called before ordinary library initializers.
// fixme defer initialization until an objc-using image is found?
if (firstTime) {
preopt_init();
}
if (PrintImages) {
_objc_inform("IMAGES: processing %u newly-mapped images...\n", mhCount);
}
// Find all images with Objective-C metadata.
hCount = 0;
// Count classes. Size various table based on the total.
int totalClasses = 0;
int unoptimizedTotalClasses = 0;
{
uint32_t i = mhCount;
I-- 按照從大到小遍歷在編譯的過(guò)程中Test1是最后的
while (i--) {
const headerType *mhdr = (const headerType *)mhdrs[I];
auto hi = addHeader(mhdr, mhPaths[i], totalClasses, unoptimizedTotalClasses);
if (!hi) {
// no objc data in this entry
continue;
}
if (mhdr->filetype == MH_EXECUTE) {
// Size some data structures based on main executable's size
#if __OBJC2__
// If dyld3 optimized the main executable, then there shouldn't
// be any selrefs needed in the dynamic map so we can just init
// to a 0 sized map
if ( !hi->hasPreoptimizedSelectors() ) {
size_t count;
_getObjc2SelectorRefs(hi, &count);
selrefCount += count;
_getObjc2MessageRefs(hi, &count);
selrefCount += count;
}
#else
_getObjcSelectorRefs(hi, &selrefCount);
#endif
#if SUPPORT_GC_COMPAT
// Halt if this is a GC app.
if (shouldRejectGCApp(hi)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"Objective-C garbage collection "
"is no longer supported.");
}
#endif
}
hList[hCount++] = hi; 這個(gè)時(shí)候把Test1放到了首位东跪。然后Test2 最后Test
if (PrintImages) {
_objc_inform("IMAGES: loading image for %s%s%s%s%s\n",
hi->fname(),
mhdr->filetype == MH_BUNDLE ? " (bundle)" : "",
hi->info()->isReplacement() ? " (replacement)" : "",
hi->info()->hasCategoryClassProperties() ? " (has class properties)" : "",
hi->info()->optimizedByDyld()?" (preoptimized)":"");
}
}
}
// Perform one-time runtime initialization that must be deferred until
// the executable itself is found. This needs to be done before
// further initialization.
// (The executable may not be present in this infoList if the
// executable does not contain Objective-C code but Objective-C
// is dynamically loaded later.
if (firstTime) {
sel_init(selrefCount);
arr_init();
#if SUPPORT_GC_COMPAT
// Reject any GC images linked to the main executable.
// We already rejected the app itself above.
// Images loaded after launch will be rejected by dyld.
for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[I];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE && shouldRejectGCImage(mh)) {
_objc_fatal_with_reason
(OBJC_EXIT_REASON_GC_NOT_SUPPORTED,
OS_REASON_FLAG_CONSISTENT_FAILURE,
"%s requires Objective-C garbage collection "
"which is no longer supported.", hi->fname());
}
}
#endif
#if TARGET_OS_OSX
// Disable +initialize fork safety if the app is too old (< 10.13).
// Disable +initialize fork safety if the app has a
// __DATA,__objc_fork_ok section.
if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) {
DisableInitializeForkSafety = true;
if (PrintInitializing) {
_objc_inform("INITIALIZE: disabling +initialize fork "
"safety enforcement because the app is "
"too old.)");
}
}
for (uint32_t i = 0; i < hCount; i++) {
auto hi = hList[I];
auto mh = hi->mhdr();
if (mh->filetype != MH_EXECUTE) continue;
unsigned long size;
if (getsectiondata(hi->mhdr(), "__DATA", "__objc_fork_ok", &size)) {
DisableInitializeForkSafety = true;
if (PrintInitializing) {
_objc_inform("INITIALIZE: disabling +initialize fork "
"safety enforcement because the app has "
"a __DATA,__objc_fork_ok section");
}
}
break; // assume only one MH_EXECUTE image
}
#endif
}
if (hCount > 0) {
_read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
}
firstTime = NO;
// Call image load funcs after everything is set up.
for (auto func : loadImageFuncs) {
for (uint32_t i = 0; i < mhCount; i++) {
func(mhdrs[I]);
}
}
}
二、Category和class Extension的區(qū)別是什么?
class Extension在編譯的時(shí)候,它的數(shù)據(jù)就已經(jīng)包含在類信息中.
而Category在運(yùn)行時(shí)才會(huì)將數(shù)據(jù)合并到類信息中. 在編譯的時(shí)候,它有多少個(gè)分類就會(huì)生成多少個(gè)category_t.等到運(yùn)行的時(shí)候才會(huì)通過(guò)runtime將Category的數(shù)據(jù),合并到類信息中(類對(duì)象,元類信息中).