OCランタイムのまとめ--クラスとオブジェクト

9608 ワード

runtimeとは?
runtimeはOCの最下層のC言語ライブラリ()であり、基本的にはCとアセンブリで書かれており、このライブラリはOCにオブジェクト向けの能力を持たせる.runtimeが行う事前にクラスの情報をロードし、方法の配布や転送を行うなどである.
クラスとオブジェクトの本質
class
Objective-CクラスはClassタイプで表され、実際にはobjc_を指すclass構造体のポインタ.次のように定義します.
typedef struct objc_class *Class;

objc/runtimeを表示します.h中objc_class構造体の定義は次のとおりです.
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  //   
    const char *name                        OBJC2_UNAVAILABLE;  //   
    long version                            OBJC2_UNAVAILABLE;  //       ,   0
    long info                               OBJC2_UNAVAILABLE;  //    ,            
    long instance_size                      OBJC2_UNAVAILABLE;  //          
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  //          
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  //        
    struct objc_cache *cache                OBJC2_UNAVAILABLE;  //     
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  //     
#endif

} OBJC2_UNAVAILABLE;

上記objc_class構造体定義では、主に次のフィールドに注目します.
  • isa:OCではクラス自体もオブジェクトであり、クラスのisaポインタはmetaClass(メタクラス)を指し、インスタンスオブジェクトのisaポインタはクラスを指す.
  • super_class:isaは、属するクラスを自省して特定するために使用されます.super_classは継承関係を決定します.だからsuper_クラスはクラスの親を指し、クラスがNSObjectやNSProxyなどの最上位レベルのルートクラスである場合、super_classはNULLです.
  • cache:最近使用されたメソッドをキャッシュします.受信者オブジェクトがメッセージを受信すると、isaポインタに基づいてメッセージに応答できるオブジェクトが検索されます.実際の使用では、このオブジェクトには一部の方法しか使われておらず、実際にはあまり使われていないか、まったく使えない方法が多い.この場合、メッセージが来るたびにmethodListsを巡回すると、パフォーマンスが悪くなるに違いありません.この時、cacheは役に立ちました.メソッドを呼び出すたびに、このメソッドはcacheリストにキャッシュされ、次回呼び出すときにruntimeはcacheに優先的に検索され、cacheがなければmethodListsにメソッドが検索されます.これにより、よく用いられる方法の呼び出しに対しては、呼び出しの効率が向上する.
  • version:このフィールドを使用してクラスのバージョン情報を提供します.これは、オブジェクトのシーケンス化に役立ち、異なる定義バージョンでのインスタンス変数レイアウトの変更を識別できます.

  • クラスのインスタンスの構造体objc_object
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    
    typedef struct objc_object *id;
    

    この構造体には、クラスを指すisaポインタというフィールドが1つしかありません.OCインスタンスオブジェクトにメッセージを送信すると、runtimeライブラリはインスタンスオブジェクトのisaポインタに基づいてインスタンスオブジェクトが属するクラスを見つけ、runtimeライブラリはクラスのメソッドリストおよび親クラスのメソッドリストでメッセージに対応するselectorが指すメソッドを探し、見つけたらこのメソッドを実行します.
    クラスのインスタンスオブジェクトを作成すると、割り当てられたメモリにはobjc_が含まれます.object構造体、次にクラスを親クラスからルートクラスまでのメンバー変数データです.NSObjectクラスのallocとallocWithZone:メソッド使用関数class_createInstanceでobjc_を作成するobject構造体.
    また、objc_である一般的なidタイプobject構造タイプのポインタ.その存在はC++のような汎用的ないくつかの動作を実現することができる.このタイプのオブジェクトは、C言語のvoid*ポインタタイプと少し似たような任意のオブジェクトに変換できます.
    メタクラス(Meta Class)
    前述したように、すべてのクラス自体もオブジェクトであり、このオブジェクトにメッセージを送信することができます(すなわち、クラスメソッドを呼び出すことができます).次のようになります.
    NSArray *array = [NSArray array];
    

    +arrayメッセージはNSArrayクラスに送信され、このNSArrayもオブジェクトであり、オブジェクトである以上objc_もあります.オブジェクトが属するクラスを指すisaポインタを含むobject構造体.では、これらには問題があります.NSArrayオブジェクト自体がクラスです.ここでisaポインタは何を指していますか.彼自身を指さすことはできないでしょう.+arrayメソッドを呼び出すには、このクラスのisaポインタは、これらのクラスメソッドを含むobjc_を指す必要があります.class構造体.これはmeta-class(メタクラス)の概念を引き出した.オブジェクトにメッセージを送信すると、runtimeはこのオブジェクトが属するこのクラスのメソッドリストでメソッドを検索します.一方、クラスにメッセージを送信すると、このクラスのmeta-classのメソッドリストで検索されます.
    meta-classが重要なのは、クラスのすべてのクラスメソッドが格納されているからです.各クラスには、各クラスのクラスメソッドがほぼ同じではないため、meta-classが個別に存在します.
    クラスオブジェクト関連アクション関数
    runtimeは、クラスとオブジェクトを操作するための多くの関数を提供します.クラスの操作方法の大部分はclassを接頭辞とし、オブジェクトの操作方法の大部分はobjcまたはobject_である.を選択します.以下では,これらの方法の用途に基づいてこれらの方法の使用を分類して議論する.
    クラス関連アクション関数
    観察objc_classの定義、runtimeが提供する操作クラスの方法は主にこの構造体の各フィールドに対するものである.
    (1)クラス名(name)
      //       
    const char * class_getName ( Class cls );
    

    class_の場合getName関数は、受信したclsがNilの場合、空の文字列を返します.
    (2)親(super_class)とメタ(meta-class)
    //       
    Class class_getSuperclass ( Class cls );
    
    //      Class       
    BOOL class_isMetaClass ( Class cls );
    

    class_getSuperclass関数は、clsがNilまたはclsがルートクラスの場合、Nilを返します.しかし、通常、NSObjectクラスのsuperclassメソッドを使用して、同じ目的を達成することができます.class_isMetaClass関数は、clsがメタクラスであればYESを返します.Noまたは転送されたclsがNilの場合、NOが返されます.
    (3)インスタンス変数サイズ(instance_size)
    //       
    size_t class_getInstanceSize ( Class cls );
    

    . (4)メンバー変数(ivars)および属性
    objc_classでは、すべてのメンバー変数、属性の情報がチェーンテーブルivarsに格納されます.ivarsは配列であり、配列内の各要素はIvar(変数情報)へのポインタである.runtimeは、このフィールドを操作するための豊富な関数を提供します.
    //                  
    Ivar class_getInstanceVariable ( Class cls, const char *name );
    
    //           
    Ivar class_getClassVariable ( Class cls, const char *name );
    
    //       
    BOOL class_addIvar ( Class cls, const char *name, size_t size, uint8_t alignment, const char *types );
    
    //           
    Ivar * class_copyIvarList ( Class cls, unsigned int *outCount );
    

    class_getInstanceVariable関数は、nameで指定されたメンバー変数情報を含むobjc_を返します.ivar構造体のポインタ(Ivar).
    class_getClassVariable関数は,現在Objective-Cにおけるクラス変数に関する情報は見つかっておらず,Objective-Cはクラス変数をサポートしていないと考えられる.返されるリストには、親クラスのメンバー変数と属性は含まれません.
    Objective-Cでは、既存のクラスにインスタンス変数を追加することはサポートされていません.そのため、システムライブラリが提供するクラスでも、カスタマイズしたクラスでも、メンバー変数を動的に追加することはできません.しかし、実行時にクラスを作成する場合は、メンバー変数をどのように追加すればいいのでしょうか.classを使うことができますaddIvar関数です.ただし、この方法はobjc_にしかないことに注意してください.allocateClassPair関数とobjc_registerClassPair間で呼び出されます.また、このクラスもメタクラスではありません.メンバー変数の最小バイト数は1<
    class_copyIvarList関数は、メンバー変数情報を指す配列を返します.配列内の各要素は、そのメンバー変数情報を指すobjc_です.ivar構造体のポインタ.この配列には、親クラスで宣言された変数は含まれません.outCountポインタは配列のサイズを返します.この配列を解放するためにfree()を使用する必要があることに注意してください.
    //        
    objc_property_t class_getProperty ( Class cls, const char *name );
    
    //       
    objc_property_t * class_copyPropertyList ( Class cls, unsigned int *outCount );
    
    //       
    BOOL class_addProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
    
    //       
    void class_replaceProperty ( Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
    

    (5)メソッド(methodLists)メソッド操作には主に以下の関数がある.
    //     
    BOOL class_addMethod ( Class cls, SEL name, IMP imp, const char *types );
    
    //       
    Method class_getInstanceMethod ( Class cls, SEL name );
    
    //      
    Method class_getClassMethod ( Class cls, SEL name );
    
    //          
    Method * class_copyMethodList ( Class cls, unsigned int *outCount );
    
    //        
    IMP class_replaceMethod ( Class cls, SEL name, IMP imp, const char *types );
    
    //          
    IMP class_getMethodImplementation ( Class cls, SEL name );
    IMP class_getMethodImplementation_stret ( Class cls, SEL name );
    
    //           selector
    BOOL class_respondsToSelector ( Class cls, SEL sel );
    

    クラスが存在するかどうかにかかわらず、クラスにメソッドを動的に追加できます.class_getInstanceMethod class_getClassMethod関数、class_copyMethodListとは異なり、この2つの関数は親クラスの実装を検索します.
    class_copyMethodList関数は、すべてのインスタンスメソッドを含む配列を返し、クラスメソッドを取得する必要がある場合はclass_を使用します.copyMethodList(object_getClass(cls),&count)(クラスのインスタンスメソッドはメタクラスに定義されます).このリストには、親実装のメソッドは含まれません.outCountパラメータはメソッドの数を返します.リストを取得した後、free()メソッドを使用して解放する必要があります.
    class_replaceMethod関数です.この関数の動作は、クラスにname指定のメソッドが存在しない場合、class_に似ています.addMethod関数と同様にメソッドが追加されます.クラスにnameで指定されたメソッドが既に存在する場合、method_と同様です.setImplementationのように元の方法の実装に代わる.
    class_getMethodImplementation関数は、クラスインスタンスにメッセージを送信するときに呼び出され、メソッド実装関数を指すポインタを返します.この関数はmethod_よりgetImplementation(class_getInstanceMethod(cls,name))は、より高速です.返される関数ポインタは、必ずしも方法の実際の実装ではなくruntime内部を指す関数である可能性があります.たとえば、クラスインスタンスがselectorに応答できない場合、返される関数ポインタは、ランタイムメッセージ転送メカニズムの一部になります.
    class_respondsToSelector関数では、通常、NSObjectクラスのrespondsToSelector:またはinstancesRespondToSelector:メソッドを使用して同じ目的を達成します.
    (6)プロトコル(objc_protocol_list)プロトコルに関するアクションには、次の関数があります.
    //     
    BOOL class_addProtocol ( Class cls, Protocol *protocol );
    
    //             
    BOOL class_conformsToProtocol ( Class cls, Protocol *protocol );
    
    //           
    Protocol * class_copyProtocolList ( Class cls, unsigned int *outCount );
    

    class_conformsToProtocol関数は、NSObjectクラスのconformsToProtocol:メソッドを使用して置き換えることができます.
    class_copyProtocolList関数は配列を返し、使用後にfree()を使用して手動で解放する必要があります.
    インスタンス関連アクション関数
    インスタンス操作関数は、主に作成したインスタンスオブジェクトの一連の操作関数です.この関数を使用して、インスタンスオブジェクトの変数の値など、インスタンスオブジェクトから目的の情報を取得できます.この関数のセットは、次の3つのクラスに分けることができます.
    (1)インスタンスオブジェクトを操作する関数
    //            
    id object_copy ( id obj, size_t size );
    
    //            
    id object_dispose ( id obj );
    

    クラスAとクラスBがあり、クラスBがクラスAのサブクラスであると仮定するシーンがある.クラスBは、追加の属性を追加することによってクラスAを拡張する.次に、Aクラスのインスタンスオブジェクトを作成し、実行時にこのオブジェクトをBクラスのインスタンスオブジェクトに変換して、Bクラスのプロパティにデータを追加できます.この場合、BクラスのインスタンスはAクラスのインスタンスよりも大きく、オブジェクトを配置するのに十分なスペースがないため、直接変換することはできません.この場合、次のコードに示すように、上記のいくつかの関数を使用して処理します.
    NSObject *a = [[NSObject alloc] init];
    id newB = object_copy(a, class_getInstanceSize(MyClass.class));
    object_setClass(newB, MyClass.class);
    object_dispose(a);
    

    (2)操作対象のインスタンス変数
    //             
    Ivar object_setInstanceVariable ( id obj, const char *name, void *value );
    
    //           
    Ivar object_getInstanceVariable ( id obj, const char *name, void **outValue );
    
    //                     
    void * object_getIndexedIvars ( id obj );
    
    //            
    id object_getIvar ( id obj, Ivar ivar );
    
    //            
    void object_setIvar ( id obj, Ivar ivar, id value );
    

    インスタンス変数のIvarがすでに知っている場合はobject_を呼び出します.getIvarはobject_よりgetInstanceVariable関数は速く、同じ場合object_setIvarもobject_よりsetInstanceVariableは速いです.
    (3)操作対象のクラス
    //          
    const char * object_getClassName ( id obj );
    
    //       
    Class object_getClass ( id obj );
    
    //       
    Class object_setClass ( id obj, Class cls );
    

    . 転載先:Objective-C Runtime実行時の1つ:クラスとオブジェクト