Objective-Cの:カテゴリー

逐語https://www.dazhuanlan.com/2019/08/25/5d623b90a7eb2/

 

序文

どんなにクラスのデザインを完璧か、将来の需要の進化に、いくつかの不測の事態が発生する可能性がありません。どのようにそれは、既存のクラスを拡張していますか?一般的には、継承の組み合わせとは良い選択です。しかし、Objective-Cの2.0で、また言語機能を提供カテゴリは、あなたが動的に既存のクラスに新しい振る舞いを追加することができます。今日のカテゴリは、さまざまなオープンソースのフレームワークへのAppleの公式の枠組みからObjective-Cのコードの隅々に広がっている、複合体は、どこでも、簡単なアプリケーションへの大規模なAPP、catagoryを備えています。本論文では、私は読者が利益を願って、より包括的なカテゴリの仕上げを行いました。

簡単な紹介

原則とランタイム層とカテゴリに関連する側面の実現カテゴリーの主要な分析にObjective-Cランタイムのソースコードを学習するとき、この資料には、仕上げされています。

  • 宝カテゴリの紹介を入力します
  • でも類推事項カテゴリと拡張
  • 真のカテゴリ熟読真夜中の油を燃やし
  • 原点カテゴリのロードをトレースする方法
  • 分派最後の年+負荷カテゴリと方法
  • そして、類推カテゴリを被覆する方法
  • 次のレベルと関連付けられたオブジェクトへのカテゴリ

宝カテゴリの説明を入力します

カテゴリーは、Objective-Cの2.0言語機能の後に追加され、主な役割は、カテゴリー、既存のクラスにメソッドを追加することです。また、Appleはまた、参照、他の2つの使用シナリオのカテゴリーを推奨アップルカテゴリドキュメントを

  • これは、内部の別のドキュメントのいくつかの異なるクラスで達成することができます。これにはいくつかの明白な利点があり、
    • 単一の文書は、異なるカテゴリに異なる機能に編成することができます)Bの量を減らすことができます
    • クラスは、複数の開発者が共同で行うことができます
    • カテゴリーは、オンデマンドなどがロードされるようにしたいです。
  • プライベートメソッドを宣言します

しかし、Apple推奨使用シナリオに加えて、開発者の大多数の脳は、広いオープンホールが、また、他のいくつかの使用シナリオのカテゴリを生み出しました。

  • 多重継承をシミュレート
  • パブリックプライベートメソッドのフレームワーク

特徴は、JavaScriptのような純粋な動的言語のために、あなたは「クラス」のオブジェクトの任意の時点で、または任意のメソッドとインスタンス変数を追加することができないかもしれないObjective-C言語は何ですか。しかし、ではない「ダイナミック」言語のように、これは本当に素晴らしい機能です。

でも、物事カテゴリ類推と拡張

拡張子は、匿名のカテゴリのように見えますが、カテゴリの拡張子名とほぼ完全に二つのものがあります。完全なクラスを形成するために一緒にコンパイル時のクラスの一部であり、解像度、コンパイルのドキュメントやドキュメントの先頭に@interfaceと@implementの実現に延長、それはクラスが付随して生成され、また一緒に滅びます。拡張子は一般的に、クラスの後ろに個人情報を使用されている、あなたは、このようなNSStringの拡張機能としてシステムに追加することはできませんので、クラスとしてクラスを拡張するクラスを追加するためのソースを持っている必要があります。(参照してくださいアップルのマニュアルを参照して

真のカテゴリ熟読真夜中の油を燃やし

私たちは、ランタイム層でOCのクラスとオブジェクトのすべてが構造体で表現され、カテゴリは例外ではないことを知って、ランタイム層、category_tのカテゴリ構造(この定義は、中にObjCランタイム・new.hで見つけることができます)、それが含まれています

  1. クラス名(名前)
    • クラス(CLS)
    • (instanceMethods)内のすべての追加されたクラスのカテゴリのリストに方法の例
    • 追加されたすべてのクラスメソッド(クラスメソッド)のカテゴリリスト
    • すべてのプロトコルのカテゴリの実装(プロトコル)のリスト
    • 追加されたすべてのプロパティのカテゴリ(instanceProperties)
typedef struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;
    struct method_list_t *classMethods;
    struct protocol_list_t *protocols;
    struct property_list_t *instanceProperties;
} category_t;

カテゴリの定義ができるカテゴリとして見ることができるから(インスタンスメソッドを追加することができ、クラスメソッドは、プロパティを追加し、さらにプロトコルを実現することができる)とするための(インスタンス変数を追加することはできません)ではありません。[OK]を、私たちは、なぜ物事最後にカテゴリを書くために、カテゴリを見に行きました:

MyClass.h:

#import <Foundation/Foundation.h>

@interface MyClass : NSObject

- (void)printName;

@end

@interface MyClass(MyAddition)

@property(nonatomic, copy) NSString *name;

- (void)printName;

@end

MyClass.m:

#import "MyClass.h"

@implementation MyClass

- (void)printName
{
    NSLog(@"%@",@"MyClass");
}

@end

@implementation MyClass(MyAddition)

- (void)printName
{
    NSLog(@"%@",@"MyAddition");
}

@end

私たちはどうなるのか見て最終的に打ち鳴らすコマンドカテゴリを使用します。

clang -rewrite-objc MyClass.m

まあ、我々は、(確かにTucao Appleのポイントの価値がある)の.cpp文書10ワット複数行を3Mサイズを持って、我々はすべてを無視して、私たちは最終文書には何の関係もない、私たちは、次のコードを探します。

static struct /*_method_list_t*/ {
unsigned int entsize;  // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,

};

static struct /*_prop_list_t*/ {
unsigned int entsize;  // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
name
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_MyClass;

static struct _category_t _OBJC_$_CATEGORY_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"MyClass",
0, // &OBJC_CLASS_$_MyClass,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyClass_$_MyAddition,
};
static void OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition(void ) {
_OBJC_$_CATEGORY_MyClass_$_MyAddition.cls = &OBJC_CLASS_$_MyClass;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
(void *)&OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition,
};
static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
&OBJC_CLASS_$_MyClass,
};
static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {
&OBJC_CLASS_$_MyClass,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_MyClass_$_MyAddition,
};

私たちは見ることができ、

  1. まず、コンパイラは方法の例にObjC $ _CATEGORY_INSTANCE_METHODSMyClass $ _MyAdditionとプロパティリストにObjC $ _PROP_LISTMyClass $ _MyAdditionのリストを生成し、両方の名前は、名前を命名共通のプレフィックス+クラス名+カテゴリを踏襲しているが、その中にインスタンスメソッドのリストが満たされています充填内部の属性のリストは、我々は、name属性でMyAdditionに追加するものである一方、MyAddition我々は、このカテゴリー方法のprintNameに書いています。カテゴリ名がリストのすべての種類に使用され、私たちのカテゴリ名が同じコンパイル単位で繰り返すことができないので、名前自体の背後にあるカテゴリ構造が、また、変更するには、静的な、そうでない場合があるという事実を認識する必要がありますコンパイルエラー。
    • 第二に、コンパイラは、カテゴリ自体にObjC $ _CATEGORYMyClass $ _MyAdditionを生成し、以前のカテゴリ自体を初期化するために生成されたのリストを。
    • 最後に、objc_catlistセクションコンパイラはアレイ1 L_OBJC_LABELCATEGORY category_t $(もちろん^ _ ^配列の長さに対応し、複数のカテゴリが生成されている場合)のサイズのデータ​​部に格納された、のカテゴリを動作させますロード。ここでは、実行時にロードする方法カテゴリの、終わりに近づいて上のコンパイラの仕事は、我々は次のセクションを発表しました。

原点カテゴリのロードをトレースする方法

私たちは、Objective-Cランタイムを実行すると、OCに依存していることを知っている、とランタイムライブラリおよびその他のシステムのOC、OS XとiOSはdyldの動的ロードです。よりdyldのために学生が会場ことができ、ここで

OC動作のため、などの入口(objc-os.mm文書に)次の

void _objc_init(void)
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

    // fixme defer initialization until an objc-using image is found?
    environ_init();
    tls_init();
    lock_init();
    exception_init();

    // Register for unmap first, in case some +load unmaps something
    _dyld_register_func_for_remove_image(&unmap_image);
    dyld_register_image_state_change_handler(dyld_image_state_bound,
                                             1/*batch*/, &map_images);
    dyld_register_image_state_change_handler(dyld_image_state_dependents_initialized, 0/*not batch*/, &load_images);
}

クラスカテゴリに接続されている上記は、新しい-ABI規格では、_objc_init map_imagesコールが最終的objc-runtime-new.mm内部_read_imagesメソッド内で呼び出したときに、map_imagesが発生_read_images方法であります終わり、次のコード:

// Discover categories. 
    for (EACH_HEADER) {
        category_t **catlist =
            _getObjc2CategoryList(hi, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            class_t *cls = remapClass(cat->cls);

            if (!cls) {
                // Category's target class is missing (probably weak-linked).
                // Disavow any knowledge of this category.
                catlist[i] = NULL;
                if (PrintConnecting) {
                    _objc_inform("CLASS: IGNORING category ???(%s) %p with "
                                 "missing weak-linked target class",
                                 cat->name, cat);
                }
                continue;
            }

            // Process this category. 
            // First, register the category with its target class. 
            // Then, rebuild the class's method lists (etc) if 
            // the class is realized. 
            BOOL classExists = NO;
            if (cat->instanceMethods ||  cat->protocols 
                ||  cat->instanceProperties)
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (isRealized(cls)) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category -%s(%s) %s",
                                 getName(cls), cat->name,
                                 classExists ? "on existing class" : "");
                }
            }

            if (cat->classMethods  ||  cat->protocols 
                /* ||  cat->classProperties */)
            {
                addUnattachedCategoryForClass(cat, cls->isa, hi);
                if (isRealized(cls->isa)) {
                    remethodizeClass(cls->isa);
                }
                if (PrintConnecting) {
                    _objc_inform("CLASS: found category +%s(%s)",
                                 getName(cls), cat->name);
                }
            }
        }
    }

まず、我々はcatlistは、我々が見ていない場合は、自分自身catlistロードする方法については、私たちのために用意し、前のセクションコンパイラcategory_t列に記載されている取得し、この関係カテゴリ自体が大型、興味のある学生ではありませんアップルは、以下のバイナリ形式とロード機構を研究することであってもよいです。

ログのこの事をPrintConnecting省略し、コードが理解しやすいです。

  1. カテゴリの方法として、プロトコル属性がクラスに追加します
  2. Categoryクラスは、方法およびプロトコルクラスのメタクラスに追加しました

コード内の短いコメントが、ことは注目に値する/ || cat->classProperties /、Appleはああのクラスに属性を追加する計画を持っているようです。[OK]を、私たちはその後、内側に見えた、各種リストのカテゴリはクラスにどのように最終的に追加され、この方法は、例えば例のリストを取る:上記のコードでaddUnattachedCategoryForClass、でちょうどクラスおよびカテゴリ関連マッピングを行うと、 remethodizeClassは追加事項に対処するための本当のヒーローです。

static void remethodizeClass(class_t *cls)
{
    category_list *cats;
    BOOL isMeta;

    rwlock_assert_writing(&runtimeLock);

    isMeta = isMetaClass(cls);

    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls))) {
        chained_property_list *newproperties;
        const protocol_list_t **newprotos;

        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s",
                         getName(cls), isMeta ? "(meta)" : "");
        }

        // Update methods, properties, protocols

        BOOL vtableAffected = NO;
        attachCategoryMethods(cls, cats, &vtableAffected);

        newproperties = buildPropertyList(NULL, cats, isMeta);
        if (newproperties) {
            newproperties->next = cls->data()->properties;
            cls->data()->properties = newproperties;
        }

        newprotos = buildProtocolList(cats, NULL, cls->data()->protocols);
        if (cls->data()->protocols  &&  cls->data()->protocols != newprotos) {
            _free_internal(cls->data()->protocols);
        }
        cls->data()->protocols = newprotos;

        _free_internal(cats);

        // Update method caches and vtables
        flushCaches(cls);
        if (vtableAffected) flushVtables(cls);
    }
}

そして、インスタンスのクラスを追加する方法は、このメソッドを呼び出すattachCategoryMethodsは、我々はattachCategoryMethodsになります:

static void 
attachCategoryMethods(class_t *cls, category_list *cats,
                      BOOL *inoutVtablesAffected)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    BOOL isMeta = isMetaClass(cls);
    method_list_t **mlists = (method_list_t **)
        _malloc_internal(cats->count * sizeof(*mlists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int i = cats->count;
    BOOL fromBundle = NO;
    while (i--) {
        method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= cats->list[i].fromBundle;
        }
    }

    attachMethodLists(cls, mlists, mcount, NO, fromBundle, inoutVtablesAffected);

    _free_internal(mlists);

}

attachCategoryMethodsジョブが比較的単純でやる、それだけでインスタンスメソッドのすべてのカテゴリのリストは、インスタンスメソッドの大規模なリストを構成し、その後、attachMethodListsメソッドに転送されます(私はこれを誓う私たちが^ _を見て、このセクションのコードの最後の作品です^)、この方法では、我々は唯一の短いを見て、少し長いです。

for (uint32_t m = 0;
             (scanForCustomRR || scanForCustomAWZ)  &&  m < mlist->count;
             m++)
        {
            SEL sel = method_list_nth(mlist, m)->name;
            if (scanForCustomRR  &&  isRRSelector(sel)) {
                cls->setHasCustomRR();
                scanForCustomRR = false;
            } else if (scanForCustomAWZ  &&  isAWZSelector(sel)) {
                cls->setHasCustomAWZ();
                scanForCustomAWZ = false;
            }
        }

        // Fill method list array
        newLists[newCount++] = mlist;
    .
    .
    .

    // Copy old methods to the method list array
    for (i = 0; i < oldCount; i++) {
        newLists[newCount++] = oldLists[i];
    }

注意すべき点が2つあります。

  1. カテゴリーアプローチは、元のカテゴリとクラスがmethodAを持っている場合には、追加のカテゴリが完了した後、クラスメソッドは、2つのmethodAのリストが表示されますされ、元のクラスには、いくつかのメソッドを持っている「完全に置換」されていません
  2. 私たちは通常、カテゴリーアプローチがある、同じ名前の元のクラスメソッドのうち、「カバー」と言って何をしている、カテゴリーアプローチは、新しいメソッドのリストの前にした、と元のクラスのメソッドは、リストの新しいアプローチの背後に配置されましたfindメソッドは、シーケンスのメソッドのリストを下に実行する時間を見つけることですので、限り、対応する名前を見つけるための方法であるよう、あきらめます^ _ ^、同じメソッド名の誰もが知っている後ろがあるかもしれません。

分派最後の年+負荷カテゴリと方法

私たちは、クラスおよびカテゴリに+ loadメソッドを持つことができることを知って、それから2つの問題があります。

  1. ロードクラスメソッド呼び出しを+するとき、我々は法のカテゴリを呼び出すことができ、それを宣言しましたか?
  2. だから、何ヶ月+ loadメソッドの呼び出しシーケンスはZeyangそれを何ですか?

我々はあまりにも見て上記のコードセクションの見解では、これら二つの質問のための少し直感的に見てみましょう:

我々のコードは、MyClassのとMyClassの(範疇及びカテゴリ2)二カテゴリが存在すると、2つのカテゴリのMyClassは+ Loadメソッドを添加し、そして範疇及びカテゴリ2は、MyClassののprintName方法の書かれています。以下の2つの環境変数を追加し、Xcodeで編集スキームをクリックします(loadメソッドを実行するためにログ情報を印刷し、負荷のカテゴリ、より多くの環境オプションはにObjC-private.hで見つけることができた場合):

プロジェクトを実行し、我々は唯一の次の順序で、私たちが望む情報を見つける、たくさんのことをプリントアウトするコントロールパネルが表示されます。

objc[1187]: REPLACED: -[MyClass printName] by category Category1
objc[1187]: REPLACED: -[MyClass printName] by category Category2
.
.
.
objc[1187]: LOAD: class 'MyClass' scheduled for +load
objc[1187]: LOAD: category 'MyClass(Category1)' scheduled for +load
objc[1187]: LOAD: category 'MyClass(Category2)' scheduled for +load
objc[1187]: LOAD: +[MyClass load]
.
.
.
objc[1187]: LOAD: +[MyClass(Category1) load]
.
.
.
objc[1187]: LOAD: +[MyClass(Category2) load]

したがって、上記の2つの問題が、答えは明らかです。

  1. 仕事のカテゴリに追加のカテゴリが+負荷法に先立って実行されますので、あなたは、呼び出すことができます
    • 実行順序は、最初の負荷カテゴリ、カテゴリ+は、カテゴリ+負荷実行順序は、コンパイルの順序に応じて決定されます。次のように現在のコンパイル順序は次のとおりです。

私たちは、カテゴリ1とカテゴリ2のシーケンスをコンパイルするために調整し、実行します。[OK]を、私たちは、パネルが変更された出力シーケンス制御を見ることができます:

objc[1187]: REPLACED: -[MyClass printName] by category Category2
objc[1187]: REPLACED: -[MyClass printName] by category Category1
.
.
.
objc[1187]: LOAD: class 'MyClass' scheduled for +load
objc[1187]: LOAD: category 'MyClass(Category2)' scheduled for +load
objc[1187]: LOAD: category 'MyClass(Category1)' scheduled for +load
objc[1187]: LOAD: +[MyClass load]
.
.
.
objc[1187]: LOAD: +[MyClass(Category2) load]
.
.
.
objc[1187]: LOAD: +[MyClass(Category1) load]

+負荷の実行の順序はそれが、「カバー」の方法アウトのためのそのようなですが、あなたは最初に対応するメソッドの最後のカテゴリのコンパイルを見つけるでしょう。

このセクションでは、我々は、あなたの質問に対する回答を得ることは非常に直感的な方法を使用して、興味のある学生は、ランタイムコードのOCを勉強し続けることができます。

そして、類推カテゴリを被覆する方法

上記のセクションのビューでは、我々は、このセクションだけで一つの質問について話してきた原則は以下のとおりです。

カテゴリの外に覆われて、元のクラスのメソッドを呼び出す方法?

この問題については、我々はすでに、カテゴリは完全に限り、我々は、対応するメソッドの最後の名前を見つけるために、メソッドリストに従うよう、あなたは元のクラスを呼び出すことができ、ちょうどのみカテゴリリストの方法の前に、同じ名前の元のクラスメソッドを置き換える実際にはないことを知っています方法:

Class currentClass = [MyClass class];
MyClass *my = [[MyClass alloc] init];

if (currentClass) {
    unsigned int methodCount;
    Method *methodList = class_copyMethodList(currentClass, &methodCount);
    IMP lastImp = NULL;
    SEL lastSel = NULL;
    for (NSInteger i = 0; i < methodCount; i++) {
        Method method = methodList[i];
        NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(method)) 
                                        encoding:NSUTF8StringEncoding];
        if ([@"printName" isEqualToString:methodName]) {
            lastImp = method_getImplementation(method);
            lastSel = method_getName(method);
        }
    }
    typedef void (*fn)(id,SEL);

    if (lastImp != NULL) {
        fn f = (fn)lastImp;
        f(my,lastSel);
    }
    free(methodList);
}

次のレベルと関連付けられたオブジェクトへのカテゴリ

上で見たように、我々は、インスタンス変数がカテゴリに追加することができないカテゴリがあることを知っています。しかし、我々は多くの場合、この時間は関連する目的を達成するために助けることができ、カテゴリ内の値と関連するオブジェクトを追加する必要があります。

MyClassの+ Category1.h:

#import "MyClass.h"

@interface MyClass (Category1)

@property(nonatomic,copy) NSString *name;

@end

MyClassの+ Category1.m:

#import "MyClass+Category1.h"
#import <objc/runtime.h>

@implementation MyClass (Category1)

+ (void)load
{
    NSLog(@"%@",@"load in Category1");
}

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self,
                             "name",
                             name,
                             OBJC_ASSOCIATION_COPY);
}

- (NSString*)name
{
    NSString *nameObject = objc_getAssociatedObject(self, "name");
    return nameObject;
}

@end

しかし、また、オブジェクトが存在して何が関連していますか?保存するには?関連するオブジェクトがときに、オブジェクトを破壊しているとどのように対処するには?私たちは、実行時にソースコードを見て、objc-references.mm文書内のメソッド_object_set_associative_referenceがあります:

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                _class_setInstancesHaveAssociatedObjects(_object_getClass(object));
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

私たちは、次のように定義されたAssociationsManager管理、およびAssociationsManagerによって関連付けられているオブジェクトのすべてを見ることができます:

class AssociationsManager {
    static OSSpinLock _lock;
    static AssociationsHashMap *_map;               // associative references:  object pointer -> PtrPtrHashMap.
public:
    AssociationsManager()   { OSSpinLockLock(&_lock); }
    ~AssociationsManager()  { OSSpinLockUnlock(&_lock); }

    AssociationsHashMap &associations() {
        if (_map == NULL)
            _map = new AssociationsHashMap();
        return *_map;
    }
};

すべての関連するオブジェクトを格納する静的AssociationsHashMapから構成されているAssociationsManager。これは、オブジェクトが内部グローバルマップ存在している関連付けられているすべてのオブジェクトと等価です。そしてキー(任意の2つの異なるオブジェクトが異なっていなければならないポインタアドレス)オブジェクトのアドレスをマップへのポインタであり、この値は、関連するオブジェクトKVペアを保持AssociationsHashMapの別のマップです。内部の論理オブジェクトの破壊では、objc-runtime-new.mm参照してください。

void *objc_destructInstance(id obj) 
{
    if (obj) {
        Class isa_gen = _object_getClass(obj);
        class_t *isa = newcls(isa_gen);

        // Read all of the flags at once for performance.
        bool cxx = hasCxxStructors(isa);
        bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);

        // This order is important.
        if (cxx) object_cxxDestruct(obj);
        if (assoc) _object_remove_assocations(obj);

        if (!UseGC) objc_clear_deallocating(obj);
    }

    return obj;
}

ああ、オブジェクトが関連オブジェクトを持っていないかを決定しますobjc_destructInstanceランタイム関数そのオブジェクトを破壊し、そうであれば、_object_remove_assocationsは清掃作業に関連するオブジェクトを行い呼び出します。

追伸

Appleのココアタッチフレームワークはオープンソースではなく、実行時のObjective-CおよびCore Foundationのがで完全にオープンソース(ですが、「ソースの前に、非秘密」 -ミスター侯杰の話として//www.opensourceます。http .apple.com / tarボール/)すべてのオープンソースをダウンロードすることができます。このシリーズは、学生が学ぶためにソースコードをダウンロードするための独自の、上記のウェブサイトを十分に得ることができ、学習のランタイムソースを更新していきます。Tバンクは、単純な、間違っている場合は、私に希望を修正します。

 

 

 

 

 

おすすめ

転載: www.cnblogs.com/petewell/p/11408232.html