Ivar objc_property_t Protocol解读

群组
Flutter,iOS,Android,Python等,闲聊工作&技术&问题;
个人主页:https://waitwalker.cn
telegram群链接:https://t.me/joinchat/Ej0o0A1ntlq5ZFIMzzO5Pw

Ivar声明

在objc-private.h文件中我们可以看到Ivar的声明结构:

typedef struct ivar_t *Ivar;

在objc-runtime-new.h中可以找到ivar_t的完整结构声明:

// MARK: - 成员变量结构声明
struct ivar_t {
#if __x86_64__
    // *offset was originally 64-bit on some x86_64 platforms.
    // We read and write only 32 bits of it.
    // Some metadata provides all 64 bits. This is harmless for unsigned
    // little-endian values.
    // Some code uses all 64 bits. class_addIvar() over-allocates the
    // offset for their benefit.
#endif
    int32_t *offset;
    const char *name;//成员变量名称
    const char *type;//成员变量类型
    // alignment is sometimes -1; use alignment() instead
    uint32_t alignment_raw;
    uint32_t size;

    uint32_t alignment() const {
        if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
        return 1 << alignment_raw;
    }
};

Ivar相关接口

在runtime.h文件:

修改一个类的成员变量的值

// MARK: - 修改一个类的成员变量的值
/**
 * Changes the value of an instance variable of a class instance.
 *
 * @param obj A pointer to an instance of a class. Pass the object containing
 *  the instance variable whose value you wish to modify.
 * @param name A C string. Pass the name of the instance variable whose value you wish to modify.
 * @param value The new value for the instance variable.
 *
 * @return A pointer to the \c Ivar data structure that defines the type and
 *  name of the instance variable specified by \e name.
 *
 * @note Instance variables with known memory management (such as ARC strong and weak)
 *  use that memory management. Instance variables with unknown memory management
 *  are assigned as if they were unsafe_unretained.
 */
OBJC_EXPORT Ivar _Nullable
object_setInstanceVariable(id _Nullable obj, const char * _Nonnull name,
                           void * _Nullable value)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
    OBJC_ARC_UNAVAILABLE;

其实现在objc-class.mm文件中,其内部调用了一个私有函数:

// 修改一个类的成员变量的值
Ivar object_setInstanceVariable(id obj, const char *name, void *value)
{
    return _object_setInstanceVariable(obj, name, value, false);
}

// 修改一个成员变量的值
static ALWAYS_INLINE
Ivar _object_setInstanceVariable(id obj, const char *name, void *value,
                                 bool assumeStrong)
{
    Ivar ivar = nil;

    if (obj  &&  name  &&  !obj->isTaggedPointer()) {
        // 首先根据成员变量名称从ivar_list中获取ivar
        if ((ivar = _class_getVariable(obj->ISA(), name))) {

            // 更新ivar的value
            _object_setIvar(obj, ivar, (id)value, assumeStrong);
        }
    }
    return ivar;
}

首先根据成员变量名称从ivar_list中获取ivar:

// MARK: - 根据成员变量名称获取成员变量
/***********************************************************************
* _class_getVariable
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar
_class_getVariable(Class cls, const char *name)
{
    mutex_locker_t lock(runtimeLock);

    for ( ; cls; cls = cls->superclass) {
        ivar_t *ivar = getIvar(cls, name);
        if (ivar) {
            return ivar;
        }
    }

    return nil;
}

// MARK: - 根据成员变量名称获取成员变量
/***********************************************************************
* getIvar
* Look up an ivar by name.
* Locking: runtimeLock must be read- or write-locked by the caller.
**********************************************************************/
static ivar_t *getIvar(Class cls, const char *name)
{
    runtimeLock.assertLocked();

    const ivar_list_t *ivars;
    assert(cls->isRealized());
    if ((ivars = cls->data()->ro->ivars)) {
        for (auto& ivar : *ivars) {
            if (!ivar.offset) continue;  // anonymous bitfield

            // ivar.name may be nil for anonymous bitfields etc.
            if (ivar.name  &&  0 == strcmp(name, ivar.name)) {
                return &ivar;
            }
        }
    }

    return nil;
}

获取到成员变量后,更新ivar的value:

// MARK: - 更新ivar的值
static ALWAYS_INLINE
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");
    }
}

更新值的规则主要根据ivar的内存管理语义:

/*
  "Unknown" includes non-object ivars and non-ARC non-__weak ivars
  "Strong" includes ARC __strong ivars
  "Weak" includes ARC and new MRC __weak ivars
  "Unretained" includes ARC __unsafe_unretained and old GC+MRC __weak ivars
*/
typedef enum {
    objc_ivar_memoryUnknown,     // unknown / unknown
    objc_ivar_memoryStrong,      // direct access / objc_storeStrong
    objc_ivar_memoryWeak,        // objc_loadWeak[Retained] / objc_storeWeak
    objc_ivar_memoryUnretained   // direct access / direct access
} objc_ivar_memory_management_t;

strong修饰的直接调用objc_storeStrong,weak调用objc_storeWeak,unretained修饰的直接赋值.这个在以后内存管理中细说.

修改一个strong修饰的ivar的值

// MARK: - 修改一个strong修饰的ivar的值
/**
 * Changes the value of an instance variable of a class instance.
 *
 * @param obj A pointer to an instance of a class. Pass the object containing
 *  the instance variable whose value you wish to modify.
 * @param name A C string. Pass the name of the instance variable whose value you wish to modify.
 * @param value The new value for the instance variable.
 *
 * @return A pointer to the \c Ivar data structure that defines the type and
 *  name of the instance variable specified by \e name.
 *
 * @note Instance variables with known memory management (such as ARC strong and weak)
 *  use that memory management. Instance variables with unknown memory management
 *  are assigned as if they were strong.
 */
OBJC_EXPORT Ivar _Nullable
object_setInstanceVariableWithStrongDefault(id _Nullable obj,
                                            const char * _Nonnull name,
                                            void * _Nullable value)
    OBJC_AVAILABLE(10.12, 10.0, 10.0, 3.0, 2.0)
    OBJC_ARC_UNAVAILABLE;

其过程与上一个类似.

获取一个类的实例的成员变量(的值)

// MARK: - 获取一个类的实例的成员变量(的值)
/**
 * Obtains the value of an instance variable of a class instance.
 *
 * @param obj A pointer to an instance of a class. Pass the object containing
 *  the instance variable whose value you wish to obtain.
 * @param name A C string. Pass the name of the instance variable whose value you wish to obtain.
 * @param outValue On return, contains a pointer to the value of the instance variable.
 *
 * @return A pointer to the \c Ivar data structure that defines the type and name of
 *  the instance variable specified by \e name.
 */
OBJC_EXPORT Ivar _Nullable
object_getInstanceVariable(id _Nullable obj, const char * _Nonnull name,
                           void * _Nullable * _Nullable outValue)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
    OBJC_ARC_UNAVAILABLE;

// 获取实例的成员变量
Ivar object_getInstanceVariable(id obj, const char *name, void **value)
{
    if (obj  &&  name  &&  !obj->isTaggedPointer()) {
        Ivar ivar;
        if ((ivar = class_getInstanceVariable(obj->ISA(), name))) {
            if (value) *value = (void *)object_getIvar(obj, ivar);
            return ivar;
        }
    }
    if (value) *value = nil;
    return nil;
}

获取一个类所有成员变量的size

// MARK: - 获取一个类所有成员变量的size
/**
 * Returns the size of instances of a class.
 *
 * @param cls A class object.
 *
 * @return The size in bytes of instances of the class \e cls, or \c 0 if \e cls is \c Nil.
 */
OBJC_EXPORT size_t
class_getInstanceSize(Class _Nullable cls)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取一个类所有成员变量的size
size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}

// Class's ivar size rounded up to a pointer-size boundary.
    uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }

 // May be unaligned depending on class's ivars.
    uint32_t unalignedInstanceSize() {
        assert(isRealized());
        // 返回一个类中所有成员变量的size
        return data()->ro->instanceSize;
    }

这里也印证了我们之前文章所说的一个类的成员变量在编译后内存布局已经固定了,其获取大小是从data()->ro->instanceSize来.

根据成员变量名称获取某个类中的成员变量(objc_getInstanceVariable中调用的就是此函数)

// MARK: - 根据成员变量名称获取某个类中的成员变量(objc_getInstanceVariable中调用的就是此函数)
/**
 * Returns the \c Ivar for a specified instance variable of a given class.
 *
 * @param cls The class whose instance variable you wish to obtain.
 * @param name The name of the instance variable definition to obtain.
 *
 * @return A pointer to an \c Ivar data structure containing information about
 *  the instance variable specified by \e name.
 */
OBJC_EXPORT Ivar _Nullable
class_getInstanceVariable(Class _Nullable cls, const char * _Nonnull name)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

// MARK: - 根据名称获取一个类中的成员变量
/***********************************************************************
* class_getInstanceVariable.  Return the named instance variable.
**********************************************************************/
Ivar class_getInstanceVariable(Class cls, const char *name)
{
    if (!cls  ||  !name) return nil;

    return _class_getVariable(cls, name);
}

通过名称获取类成员变量

// MARK: - 通过名称获取类成员变量
/**
 * Returns the Ivar for a specified class variable of a given class.
 *
 * @param cls The class definition whose class variable you wish to obtain.
 * @param name The name of the class variable definition to obtain.
 *
 * @return A pointer to an \c Ivar data structure containing information about the class variable specified by \e name.
 */
OBJC_EXPORT Ivar _Nullable
class_getClassVariable(Class _Nullable cls, const char * _Nonnull name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取类中的类成员变量
/***********************************************************************
* class_getClassVariable.  Return the named class variable.
**********************************************************************/
Ivar class_getClassVariable(Class cls, const char *name)
{
    if (!cls) return nil;

    return class_getInstanceVariable(cls->ISA(), name);
}

获取一个类的所有成员变量

// MARK: - 获取一个类的所有成员变量
/**
 * Describes the instance variables declared by a class.
 *
 * @param cls The class to inspect.
 * @param outCount On return, contains the length of the returned array.
 *  If outCount is NULL, the length is not returned.
 *
 * @return An array of pointers of type Ivar describing the instance variables declared by the class.
 *  Any instance variables declared by superclasses are not included. The array contains *outCount
 *  pointers followed by a NULL terminator. You must free the array with free().
 *
 *  If the class declares no instance variables, or cls is Nil, NULL is returned and *outCount is 0.
 */
OBJC_EXPORT Ivar _Nonnull * _Nullable
class_copyIvarList(Class _Nullable cls, unsigned int * _Nullable outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取一个类的所有成员变量
/***********************************************************************
* class_copyIvarList
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
Ivar *
class_copyIvarList(Class cls, unsigned int *outCount)
{
    const ivar_list_t *ivars;
    Ivar *result = nil;
    unsigned int count = 0;

    if (!cls) {
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    assert(cls->isRealized());

    if ((ivars = cls->data()->ro->ivars)  &&  ivars->count) {
        result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));

        for (auto& ivar : *ivars) {
            if (!ivar.offset) continue;  // anonymous bitfield
            result[count++] = &ivar;
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    return result;
}

其获取主要来自:cls->data()->ro->ivars.

给一个类添加成员变量:必须在调用objc_allocateClassPair之后在objc_registerClassPair之前

// MARK: - 给一个类添加成员变量:必须在调用objc_allocateClassPair之后在objc_registerClassPair之前
/**
 * Adds a new instance variable to a class.
 *
 * @return YES if the instance variable was added successfully, otherwise NO
 *         (for example, the class already contains an instance variable with that name).
 *
 * @note This function may only be called after objc_allocateClassPair and before objc_registerClassPair.
 *       Adding an instance variable to an existing class is not supported.
 * @note The class must not be a metaclass. Adding an instance variable to a metaclass is not supported.
 * @note The instance variable's minimum alignment in bytes is 1<<align. The minimum alignment of an instance
 *       variable depends on the ivar's type and the machine architecture.
 *       For variables of any pointer type, pass log2(sizeof(pointer_type)).
 */
OBJC_EXPORT BOOL
class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size,
              uint8_t alignment, const char * _Nullable types)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 给一个类添加成员变量
/***********************************************************************
* class_addIvar
* Adds an ivar to a class.
* Locking: acquires runtimeLock
**********************************************************************/
BOOL
class_addIvar(Class cls, const char *name, size_t size,
              uint8_t alignment, const char *type)
{
    if (!cls) return NO;

    if (!type) type = "";
    if (name  &&  0 == strcmp(name, "")) name = nil;

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);
    assert(cls->isRealized());

    // No class variables
    if (cls->isMetaClass()) {
        return NO;
    }

    // Can only add ivars to in-construction classes.
    if (!(cls->data()->flags & RW_CONSTRUCTING)) {
        return NO;
    }

    // Check for existing ivar with this name, unless it's anonymous.
    // Check for too-big ivar.
    // fixme check for superclass ivar too?
    if ((name  &&  getIvar(cls, name))  ||  size > UINT32_MAX) {
        return NO;
    }

    class_ro_t *ro_w = make_ro_writeable(cls->data());

    // fixme allocate less memory here

    ivar_list_t *oldlist, *newlist;
    if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) {
        size_t oldsize = oldlist->byteSize();
        newlist = (ivar_list_t *)calloc(oldsize + oldlist->entsize(), 1);
        memcpy(newlist, oldlist, oldsize);
        free(oldlist);
    } else {
        newlist = (ivar_list_t *)calloc(sizeof(ivar_list_t), 1);
        newlist->entsizeAndFlags = (uint32_t)sizeof(ivar_t);
    }

    uint32_t offset = cls->unalignedInstanceSize();
    uint32_t alignMask = (1<<alignment)-1;
    offset = (offset + alignMask) & ~alignMask;

    ivar_t& ivar = newlist->get(newlist->count++);
#if __x86_64__
    // Deliberately over-allocate the ivar offset variable.
    // Use calloc() to clear all 64 bits. See the note in struct ivar_t.
    ivar.offset = (int32_t *)(int64_t *)calloc(sizeof(int64_t), 1);
#else
    ivar.offset = (int32_t *)malloc(sizeof(int32_t));
#endif
    *ivar.offset = offset;
    ivar.name = name ? strdupIfMutable(name) : nil;
    ivar.type = strdupIfMutable(type);
    ivar.alignment_raw = alignment;
    ivar.size = (uint32_t)size;

    ro_w->ivars = newlist;
    cls->setInstanceSize((uint32_t)(offset + size));

    // Ivar layout updated in registerClass.

    return YES;
}

获取成员变量名称

// MARK: - 获取成员变量名称
/* Working with Instance Variables */

/**
 * Returns the name of an instance variable.
 *
 * @param v The instance variable you want to enquire about.
 *
 * @return A C string containing the instance variable's name.
 */
OBJC_EXPORT const char * _Nullable
ivar_getName(Ivar _Nonnull v)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取成员变量名称
/***********************************************************************
* ivar_getName
* fixme
* Locking: none
**********************************************************************/
const char *
ivar_getName(Ivar ivar)
{
    if (!ivar) return nil;
    return ivar->name;
}

获取成员变量类型

// MARK: - 获取成员变量类型
/**
 * Returns the type string of an instance variable.
 *
 * @param v The instance variable you want to enquire about.
 *
 * @return A C string containing the instance variable's type encoding.
 *
 * @note For possible values, see Objective-C Runtime Programming Guide > Type Encodings.
 */
OBJC_EXPORT const char * _Nullable
ivar_getTypeEncoding(Ivar _Nonnull v)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取成员变量名称
/***********************************************************************
* ivar_getTypeEncoding
* fixme
* Locking: none
**********************************************************************/
const char *
ivar_getTypeEncoding(Ivar ivar)
{
    if (!ivar) return nil;
    return ivar->type;
}

property声明

/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;

完整声明在objc-runtime-new.h文件中:

// MARK: - 属性声明
struct property_t {
    const char *name;
    const char *attributes;
};

根据名称获取一个类的属性

// MARK: - 根据名称获取一个类的属性
/**
 * Returns a property with a given name of a given class.
 *
 * @param cls The class you want to inspect.
 * @param name The name of the property you want to inspect.
 *
 * @return A pointer of type \c objc_property_t describing the property, or
 *  \c NULL if the class does not declare a property with that name,
 *  or \c NULL if \e cls is \c Nil.
 */
OBJC_EXPORT objc_property_t _Nullable
class_getProperty(Class _Nullable cls, const char * _Nonnull name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 根据名称获取一个类中的属性
/***********************************************************************
* class_getProperty
* fixme
* Locking: read-locks runtimeLock
**********************************************************************/
objc_property_t class_getProperty(Class cls, const char *name)
{
    if (!cls  ||  !name) return nil;

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);

    assert(cls->isRealized());

    for ( ; cls; cls = cls->superclass) {
        for (auto& prop : cls->data()->properties) {
            if (0 == strcmp(name, prop.name)) {
                return (objc_property_t)&prop;
            }
        }
    }

    return nil;
}

先判断cls有没有实现,然后可以看到其获取时从可变class_rw_t的成员变量properties中获得:cls->data()->properties.

获取一个类的属性列表

// MARK: - 获取一个类的属性列表
/**
 * Describes the properties declared by a class.
 *
 * @param cls The class you want to inspect.
 * @param outCount On return, contains the length of the returned array.
 *  If \e outCount is \c NULL, the length is not returned.
 *
 * @return An array of pointers of type \c objc_property_t describing the properties
 *  declared by the class. Any properties declared by superclasses are not included.
 *  The array contains \c *outCount pointers followed by a \c NULL terminator. You must free the array with \c free().
 *
 *  If \e cls declares no properties, or \e cls is \c Nil, returns \c NULL and \c *outCount is \c 0.
 */
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
class_copyPropertyList(Class _Nullable cls, unsigned int * _Nullable outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取一个类的属性列表
/***********************************************************************
* class_copyPropertyList. Returns a heap block containing the
* properties declared in the class, or nil if the class
* declares no properties. Caller must free the block.
* Does not copy any superclass's properties.
* Locking: read-locks runtimeLock
**********************************************************************/
objc_property_t *
class_copyPropertyList(Class cls, unsigned int *outCount)
{
    if (!cls) {
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    checkIsKnownClass(cls);
    assert(cls->isRealized());

    // 先获取rw
    auto rw = cls->data();

    property_t **result = nil;
    unsigned int count = rw->properties.count();
    if (count > 0) {
        result = (property_t **)malloc((count + 1) * sizeof(property_t *));

        count = 0;
        for (auto& prop : rw->properties) {
            result[count++] = &prop;
        }
        result[count] = nil;
    }

    if (outCount) *outCount = count;
    return (objc_property_t *)result;
}

给一个类添加属性

// MARK: - 给一个类添加属性
/**
 * Adds a property to a class.
 *
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \e attributes.
 *
 * @return \c YES if the property was added successfully, otherwise \c NO
 *  (for example, the class already has that property).
 */
OBJC_EXPORT BOOL
class_addProperty(Class _Nullable cls, const char * _Nonnull name,
                  const objc_property_attribute_t * _Nullable attributes,
                  unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);

// 给一个类添加属性
BOOL
class_addProperty(Class cls, const char *name,
                  const objc_property_attribute_t *attrs, unsigned int n)
{
    return _class_addProperty(cls, name, attrs, n, NO);
}

// 给一个类添加属性
/***********************************************************************
* class_addProperty
* Adds a property to a class.
* Locking: acquires runtimeLock
**********************************************************************/
static bool
_class_addProperty(Class cls, const char *name,
                   const objc_property_attribute_t *attrs, unsigned int count,
                   bool replace)
{
    if (!cls) return NO;
    if (!name) return NO;

    property_t *prop = class_getProperty(cls, name);
    if (prop  &&  !replace) {
        // already exists, refuse to replace
        return NO;
    }
    else if (prop) {
        // replace existing
        mutex_locker_t lock(runtimeLock);
        try_free(prop->attributes);
        prop->attributes = copyPropertyAttributeString(attrs, count);
        return YES;
    }
    else {
        mutex_locker_t lock(runtimeLock);

        assert(cls->isRealized());

        property_list_t *proplist = (property_list_t *)
            malloc(sizeof(*proplist));
        proplist->count = 1;
        proplist->entsizeAndFlags = sizeof(proplist->first);
        proplist->first.name = strdupIfMutable(name);
        proplist->first.attributes = copyPropertyAttributeString(attrs, count);

        cls->data()->properties.attachLists(&proplist, 1);

        return YES;
    }
}

其操作的还是class_rw_t中的成员变量properties;侧面印证class_rw_t是可变的.

更新某个属性值

// MARK: - 更新某个属性值
/**
 * Replace a property of a class.
 *
 * @param cls The class to modify.
 * @param name The name of the property.
 * @param attributes An array of property attributes.
 * @param attributeCount The number of attributes in \e attributes.
 */
OBJC_EXPORT void
class_replaceProperty(Class _Nullable cls, const char * _Nonnull name,
                      const objc_property_attribute_t * _Nullable attributes,
                      unsigned int attributeCount)
    OBJC_AVAILABLE(10.7, 4.3, 9.0, 1.0, 2.0);

// 更新某个属性
void
class_replaceProperty(Class cls, const char *name,
                      const objc_property_attribute_t *attrs, unsigned int n)
{
    _class_addProperty(cls, name, attrs, n, YES);
}

获取属性名称

// MARK: - 获取属性名称
/* Working with Properties */

/**
 * Returns the name of a property.
 *
 * @param property The property you want to inquire about.
 *
 * @return A C string containing the property's name.
 */
OBJC_EXPORT const char * _Nonnull
property_getName(objc_property_t _Nonnull property)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取属性名称
const char *property_getName(objc_property_t prop)
{
    return prop->name;
}

获取属性的内存管理语义相关

// MARK: - 获取属性的内存管理语义相关
/**
 * Returns the attribute string of a property.
 *
 * @param property A property.
 *
 * @return A C string containing the property's attributes.
 *
 * @note The format of the attribute string is described in Declared Properties in Objective-C Runtime Programming Guide.
 */
OBJC_EXPORT const char * _Nullable
property_getAttributes(objc_property_t _Nonnull property)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取属性的内存管理语义
const char *property_getAttributes(objc_property_t prop)
{
    return prop->attributes;
}

这个获取的到的就是readwrite,stong,atomic等相关.

Protocol的声明

#ifdef __OBJC__
@class Protocol;
#else
typedef struct objc_object Protocol;
#endif

@implementation Protocol

#if __OBJC2__
// fixme hack - make Protocol a non-lazy class
+ (void) load { }
#endif


- (BOOL) conformsTo: (Protocol *)aProtocolObj
{
    return protocol_conformsToProtocol(self, aProtocolObj);
}

- (struct objc_method_description *) descriptionForInstanceMethod:(SEL)aSel
{
#if !__OBJC2__
    return lookup_protocol_method((struct old_protocol *)self, aSel,
                                  YES/*required*/, YES/*instance*/,
                                  YES/*recursive*/);
#else
    return method_getDescription(protocol_getMethod((struct protocol_t *)self,
                                                     aSel, YES, YES, YES));
#endif
}

- (struct objc_method_description *) descriptionForClassMethod:(SEL)aSel
{
#if !__OBJC2__
    return lookup_protocol_method((struct old_protocol *)self, aSel,
                                  YES/*required*/, NO/*instance*/,
                                  YES/*recursive*/);
#else
    return method_getDescription(protocol_getMethod((struct protocol_t *)self,
                                                    aSel, YES, NO, YES));
#endif
}

- (const char *)name
{
    return protocol_getName(self);
}

- (BOOL)isEqual:other
{
#if __OBJC2__
    // check isKindOf:
    Class cls;
    Class protoClass = objc_getClass("Protocol");
    for (cls = object_getClass(other); cls; cls = cls->superclass) {
        if (cls == protoClass) break;
    }
    if (!cls) return NO;
    // check equality
    return protocol_isEqual(self, other);
#else
    return [other isKindOf:[Protocol class]] && [self conformsTo: other] && [other conformsTo: self];
#endif
}

#if __OBJC2__
- (NSUInteger)hash
{
    return 23;
}
#else
- (unsigned)hash
{
    return 23;
}
#endif

@end

在runtime.h文件中我们可以看到Protocol被定义为一个类继承自上帝类NSObject.这个类并没有对外暴露什么接口.下面我们看一下protocol_t的完整声明:

// MARK: - 协议的声明结构
struct protocol_t : objc_object {
    const char *mangledName;
    struct protocol_list_t *protocols; //协议列表
    method_list_t *instanceMethods;//实例方法列表
    method_list_t *classMethods;//类方法列表
    method_list_t *optionalInstanceMethods;//可选的实例方法列表
    method_list_t *optionalClassMethods;//可选的类方法列表
    property_list_t *instanceProperties;//属性列表
    uint32_t size;   // sizeof(protocol_t)
    uint32_t flags;
    // Fields below this point are not always present on disk.
    const char **_extendedMethodTypes;
    const char *_demangledName;
    property_list_t *_classProperties;

    const char *demangledName();

    const char *nameForLogging() {
        return demangledName();
    }

    bool isFixedUp() const;
    void setFixedUp();

#   define HAS_FIELD(f) (size >= offsetof(protocol_t, f) + sizeof(f))

    bool hasExtendedMethodTypesField() const {
        return HAS_FIELD(_extendedMethodTypes);
    }
    bool hasDemangledNameField() const {
        return HAS_FIELD(_demangledName);
    }
    bool hasClassPropertiesField() const {
        return HAS_FIELD(_classProperties);
    }

#   undef HAS_FIELD

    const char **extendedMethodTypes() const {
        return hasExtendedMethodTypesField() ? _extendedMethodTypes : nil;
    }

    property_list_t *classProperties() const {
        return hasClassPropertiesField() ? _classProperties : nil;
    }
};

给一个类添加协议

// MARK: - 给一个类添加协议
/**
 * Adds a protocol to a class.
 *
 * @param cls The class to modify.
 * @param protocol The protocol to add to \e cls.
 *
 * @return \c YES if the method was added successfully, otherwise \c NO
 *  (for example, the class already conforms to that protocol).
 */
OBJC_EXPORT BOOL
class_addProtocol(Class _Nullable cls, Protocol * _Nonnull protocol)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 给一个类添加协议
/***********************************************************************
* class_addProtocol
* Adds a protocol to a class.
* Locking: acquires runtimeLock
**********************************************************************/
BOOL class_addProtocol(Class cls, Protocol *protocol_gen)
{
    protocol_t *protocol = newprotocol(protocol_gen);

    if (!cls) return NO;
    if (class_conformsToProtocol(cls, protocol_gen)) return NO;

    mutex_locker_t lock(runtimeLock);

    assert(cls->isRealized());

    // fixme optimize
    protocol_list_t *protolist = (protocol_list_t *)
        malloc(sizeof(protocol_list_t) + sizeof(protocol_t *));
    protolist->count = 1;
    protolist->list[0] = (protocol_ref_t)protocol;

    // 添加到protocols里面
    cls->data()->protocols.attachLists(&protolist, 1);

    // fixme metaclass?

    return YES;
}

其操作的还是class_rw_t中的成员变量protocols;侧面印证class_rw_t是可变的.

根据名称获取一个协议

// MARK: - 根据名称获取一个协议
/* Working with Protocols */

/**
 * Returns a specified protocol.
 *
 * @param name The name of a protocol.
 *
 * @return The protocol named \e name, or \c NULL if no protocol named \e name could be found.
 *
 * @note This function acquires the runtime lock.
 */
OBJC_EXPORT Protocol * _Nullable
objc_getProtocol(const char * _Nonnull name)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取协议
/***********************************************************************
* objc_getProtocol
* Get a protocol by name, or return nil
* Locking: read-locks runtimeLock
**********************************************************************/
Protocol *objc_getProtocol(const char *name)
{
    mutex_locker_t lock(runtimeLock);
    return getProtocol(name);
}

获取运行时所有的协议列表

// MARK: - 获取运行时所有的协议列表
/**
 * Returns an array of all the protocols known to the runtime.
 *
 * @param outCount Upon return, contains the number of protocols in the returned array.
 *
 * @return A C array of all the protocols known to the runtime. The array contains \c *outCount
 *  pointers followed by a \c NULL terminator. You must free the list with \c free().
 *
 * @note This function acquires the runtime lock.
 */
OBJC_EXPORT Protocol * __unsafe_unretained _Nonnull * _Nullable
objc_copyProtocolList(unsigned int * _Nullable outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取运行时所有的协议列表
/***********************************************************************
* objc_copyProtocolList
* Returns pointers to all protocols.
* Locking: read-locks runtimeLock
**********************************************************************/
Protocol * __unsafe_unretained *
objc_copyProtocolList(unsigned int *outCount)
{
    mutex_locker_t lock(runtimeLock);

    NXMapTable *protocol_map = protocols();

    unsigned int count = NXCountMapTable(protocol_map);
    if (count == 0) {
        if (outCount) *outCount = 0;
        return nil;
    }

    Protocol **result = (Protocol **)malloc((count+1) * sizeof(Protocol*));

    unsigned int i = 0;
    Protocol *proto;
    const char *name;
    NXMapState state = NXInitMapState(protocol_map);
    while (NXNextMapState(protocol_map, &state,
                          (const void **)&name, (const void **)&proto))
    {
        result[i++] = proto;
    }

    result[i++] = nil;
    assert(i == count+1);

    if (outCount) *outCount = count;
    return result;
}

获取某个协议中的某个属性

// MARK: - 获取某个协议中的某个属性
/**
 * Returns the specified property of a given protocol.
 *
 * @param proto A protocol.
 * @param name The name of a property.
 * @param isRequiredProperty \c YES searches for a required property, \c NO searches for an optional property.
 * @param isInstanceProperty \c YES searches for an instance property, \c NO searches for a class property.
 *
 * @return The property specified by \e name, \e isRequiredProperty, and \e isInstanceProperty for \e proto,
 *  or \c NULL if none of \e proto's properties meets the specification.
 */
OBJC_EXPORT objc_property_t _Nullable
protocol_getProperty(Protocol * _Nonnull proto,
                     const char * _Nonnull name,
                     BOOL isRequiredProperty, BOOL isInstanceProperty)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取协议中的某个属性
objc_property_t protocol_getProperty(Protocol *p, const char *name,
                              BOOL isRequiredProperty, BOOL isInstanceProperty)
{
    if (!p  ||  !name) return nil;

    mutex_locker_t lock(runtimeLock);
    return (objc_property_t)
        protocol_getProperty_nolock(newprotocol(p), name,
                                    isRequiredProperty, isInstanceProperty);
}

获取协议的属性列表

// MARK: - 获取协议的属性列表
/**
 * Returns an array of the required instance properties declared by a protocol.
 *
 * @note Identical to
 * \code
 * protocol_copyPropertyList2(proto, outCount, YES, YES);
 * \endcode
 */
OBJC_EXPORT objc_property_t _Nonnull * _Nullable
protocol_copyPropertyList(Protocol * _Nonnull proto,
                          unsigned int * _Nullable outCount)
    OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

// 获取协议属性列表
objc_property_t *
protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
{
    return protocol_copyPropertyList2(proto, outCount,
                                      YES/*required*/, YES/*instance*/);
}

// 获取协议属性列表
objc_property_t *
protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount,
                           BOOL isRequiredProperty, BOOL isInstanceProperty)
{
    if (!proto  ||  !isRequiredProperty) {
        // Optional properties are not currently supported.
        if (outCount) *outCount = 0;
        return nil;
    }

    mutex_locker_t lock(runtimeLock);

    property_list_t *plist = isInstanceProperty
        ? newprotocol(proto)->instanceProperties
        : newprotocol(proto)->classProperties();
    return (objc_property_t *)copyPropertyList(plist, outCount);
}

编译后源码库

编译后的源码放在Github, 如果对你有帮助,请给一个star吧!

博客地址&相关文章

博客地址: https://waitwalker.github.io/

系列文章:

1. Runtime源码编译

2. objc_object解读

3. Method解读

4. Class解读

5. Ivar objc_property_t Protocol解读

6. Block解读

7. Retain&Release解读

8. Autorelease解读

9. Weak解读

10. msgSend()解读


  目录