OCソースノート

9644 ワード

  • NSObjectのisaポインタがclassオブジェクトのソースコードgetclassを指す理由は、次の
  • である.
    Class object_getClass(id obj)
    {
        if (obj) return obj->getIsa();
        else return Nil;
    }
    

    OCのClassオブジェクトはobjc_class構造体のポインタtypedef struct objc_class *Class;
  • Classオブジェクトisaポインタがmeta classを指し、meta classのisaポインタがsuperClass objc_を指すと言われています.class構造体は以下のように定義され、getMetaの実装:
  • struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
          cache_t cache; //  
        class_data_bits_t bits; //  , ,protocol, 
        ...
        Class getMeta() {
            if (isMetaClass()) return (Class)this;
            else return this->ISA();
        }
    
        ...
    }
    

    この言い方は完全に正確ではありません.objc-runtimeソースコードのobjc_objectはisaポインタでクラスオブジェクトにアクセスし、objc_classは、objc_のためsuperclassポインタを使用して親クラスのclassオブジェクトにアクセスします.classはobjcから継承されます.objectは、理論的にもobjectなので、classにはisMetaClassメソッドの判断があり、bitsの識別ビットを取得することでmetaClassであるか否かを返します.もしそうであれば、クラスオブジェクトであり、自分を返します.そうでなければobjectオブジェクトと見なし、objc_を呼び出します.objectのISA()メソッドはmetaClassを取得し、meta classは名前の指代であり、メソッドである.
  • メソッド実装の検索と転送プロセスについてobjc-runtimeの実装詳細
  • IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                           bool initialize, bool cache, bool resolver)
    {
        Class curClass;
        IMP methodPC = nil;
        Method meth;
         // 
        if (cache) {
            methodPC = _cache_getImp(cls, sel);
            if (methodPC) return methodPC;    
        }
    
        // freed class  
        if (cls == _class_getFreedObjectClass())
            return (IMP) _freedHandler;
    
        // 
        {
            Method meth = getMethodNoSuper_nolock(cls, sel);
            if (meth) {
                log_and_fill_cache(cls, meth->imp, sel, inst, cls);
                imp = meth->imp;
                goto done;
            }
        }
    
        //  cache 
                {
            unsigned attempts = unreasonableClassCount();
            for (Class curClass = cls->superclass;
                 curClass != nil;
                 curClass = curClass->superclass)
            {
                // Halt if there is a cycle in the superclass chain.
                if (--attempts == 0) {
                    _objc_fatal("Memory corruption in class list.");
                }
                
                // Superclass cache.
                imp = cache_getImp(curClass, sel);
                if (imp) {
                    if (imp != (IMP)_objc_msgForward_impcache) {
                        log_and_fill_cache(cls, imp, sel, inst, curClass);
                        goto done;
                    }
                    else {
                        break;
                    }
                }
                
                // Superclass method list.
                Method meth = getMethodNoSuper_nolock(curClass, sel);
                if (meth) {
                    log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
                    imp = meth->imp;
                    goto done;
                }
            }
        }
        //  , ,  
            if (resolver  &&  !triedResolver) {
            runtimeLock.unlockRead();
            _class_resolveMethod(cls, sel, inst);
            runtimeLock.read();
            triedResolver = YES;
            goto retry;
        }
       //   imp
        imp = (IMP)_objc_msgForward_impcache;
        cache_fill(cls, sel, imp, inst);
    
     done:
        runtimeLock.unlockRead();
        return imp;
    }
    
  • loadメソッドとinitializeメソッドの違いをまとめたとき、loadはクラスがメモリにロードされたときに呼び出され、優先親->子->分類initializeはクラスが初めてメッセージを受信したときに呼び出され、優先分類->子->親
  • 書き込みコード検証loadメソッドmain関数が実行される前にこのクラスが使用されるかどうかにかかわらず、load関数は必ず呼び出されます.サブクラスがあるかどうかにかかわらず、サブクラスもloadメソッドが実装されているかどうかにかかわらず、loadメソッドは呼び出され、親クラス->サブクラス->分類が優先されます.
    ただしinitializeはmain関数の後に呼び出されます.同様に、呼び出しタイミングはobjc-runtimで見つけることができます.動的に実行すると、クラスを作成するオブジェクトobject(objectにclassを使用する必要がある一連の情報を初期化する)にアクセスするか、classのプロパティにアクセスするか、classの方法は、class情報にアクセスする場合、meta classが初期化されていないかどうかを確認します.クラスのinitializeメソッドが先に呼び出されるので、いつinitializeが呼び出されるかは、コードによって決まりますが、classを使用するとinitializeが呼び出され、使用されないと呼び出されません.classAがclassBを継承するのではなく、classAのinitializeはclassBのinitializeメソッドよりも先に呼び出されるに違いありません.コード作成時に、どのクラスのアクセスが先にトリガーされるかによって決まります.書き込みコードの検証は、親クラスのinitializeが子クラスより先に呼び出される場合も確かにある.1つのクラスにN個のサブクラスがある場合、いずれのサブクラスの作成も、cから親クラスのinitializeが呼び出されることをトリガする可能性があるが、他のサブクラスのinitializeは呼び出されない.
  • Copyが呼び出したのはcopyWithZone
  • です.
    - (id)copy
    {
        return [self copyFromZone: [self zone]];
    }
    
  • isKindOfClassとisMemberOfClassの違いisKindOfClassは親class
  • と比較します.
    - (BOOL)isKindOf:aClass
    {// 
        Class cls;
        for (cls = isa; cls; cls = cls->superclass) 
            if (cls == (Class)aClass)
                return YES;
        return NO;
    }
    
    - (BOOL)isMemberOf:aClass
    {
        return isa == (Class)aClass;
    }
    
  • strongタイプproperty
  • を実現
    void
    objc_storeStrong(id *location, id obj)
    {
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj);
        *location = obj;
        objc_release(prev);
    }
    
  • weakタイプproperty実装はweakHashTableに入れられています.setのときはretainせず、getのときはretain
  • になります.
    void _object_setIvar(id obj, Ivar ivar, id value, bool assumeStrong)
    {
        if (!obj  ||  !ivar  ||  obj->isTaggedPointer()) return;
    
        ptrdiff_t offset;
        objc_ivar_memory_management_t memoryManagement;
        _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement);
    
        if (memoryManagement == objc_ivar_memoryUnknown) {
            if (assumeStrong) memoryManagement = objc_ivar_memoryStrong;
            else memoryManagement = objc_ivar_memoryUnretained;
        }
    
        id *location = (id *)((char *)obj + offset);
    
        switch (memoryManagement) {
        case objc_ivar_memoryWeak:       objc_storeWeak(location, value); break;
        case objc_ivar_memoryStrong:     objc_storeStrong(location, value); break;
        case objc_ivar_memoryUnretained: *location = value; break;
        case objc_ivar_memoryUnknown:    _objc_fatal(“impossible”);
        }
    }
    

    デフォルトassumeStrongはno、strongでなければunsafe_unretain
  • propertyのAtomic実装プラスos_unfair_lock,atomicは,マルチスレッド読み書き属性が安全であることを保証し,読み書きロックに加えてgetpropertyの実装でobjc_を呼び出した.retainは、返されるオブジェクトが直ちに解放されないことを保証する.これも、atomic属性が読み書き操作の安全を保証する鍵である
  • である.
    static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
    {
        if (offset == 0) {
            object_setClass(self, newValue);
            return;
        }
    
        id oldValue;
        id *slot = (id*) ((char*)self + offset);
    
        if (copy) {
            newValue = [newValue copyWithZone:nil];
        } else if (mutableCopy) {
            newValue = [newValue mutableCopyWithZone:nil];
        } else {
            if (*slot == newValue) return;
            newValue = objc_retain(newValue);
        }
    
        if (!atomic) {
            oldValue = *slot;
            *slot = newValue;
        } else {
            spinlock_t& slotlock = PropertyLocks[slot];
            slotlock.lock();
            oldValue = *slot;
            *slot = newValue;        
            slotlock.unlock();
        }
    
        objc_release(oldValue);
    }
    
    id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
        if (offset == 0) {
            return object_getClass(self);
        }
    
        // Retain release world
        id *slot = (id*) ((char*)self + offset);
        if (!atomic) return *slot;
            
        // Atomic retain release world
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        id value = objc_retain(*slot);
        slotlock.unlock();
        
        // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
        return objc_autoreleaseReturnValue(value);
    }
    
  • AssociationsManager動的ランタイムバインディングを管理するグローバル変数
  • 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);
                    object->setHasAssociatedObjects();
                }
            } 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);
    }