ObjC|ObjC 学习笔记(四)(ivar)

在阅读完property_t相关代码之后,接下来学习和property_t十分相似的内容Ivar,我们就不再去做一个和上一篇文章一样的示例代码了,我们直接从Ivar的定义开始学习相关内容。

/// An opaque type that represents an instance variable. typedef struct objc_ivar *Ivar; struct objc_ivar { char * _Nullable ivar_nameOBJC2_UNAVAILABLE; char * _Nullable ivar_typeOBJC2_UNAVAILABLE; int ivar_offsetOBJC2_UNAVAILABLE; #ifdef __LP64__ int spaceOBJC2_UNAVAILABLE; #endif }

Ivar的定义可以看到Ivar有四个属性ivar_name, ivar_type, ivar_offset分别代表了属性的名称类型内存偏移量
然后我们回过头来再去阅读类的定义,可以看到objc_class中定义了一个objc_ivar_list用于存放属性,接下来我们看看objc_ivar_list的定义
struct objc_class { ... // objc存在一个objc_ivar_list的指针,用于保存属性 struct objc_ivar_list * _Nullable ivarsOBJC2_UNAVAILABLE; ...} OBJC2_UNAVAILABLE; // objc_ivar_list 定义 struct objc_ivar_list { // 属性数量 int ivar_countOBJC2_UNAVAILABLE; #ifdef __LP64__ int spaceOBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_ivar ivar_list[1]OBJC2_UNAVAILABLE; }OBJC2_UNAVAILABLE;

objc_ivar_list的定义中,可以看到定义了ivar_count用于存储属性数量,ivar_list用于存储Ivar
然后我们继续学习Ivar相关的方法,和property_t一样,我们先来看获取Ivar相关属性的方法。
ivar_getName、ivar_getTypeEncoding和ivar_getOffset 我们简单的看一下这三个方法的代码。
// 获取属性的名称 const char * ivar_getName(Ivar ivar) { if (!ivar) return nil; return ivar->name; }// 获取属性的类型 const char * ivar_getTypeEncoding(Ivar ivar) { if (!ivar) return nil; return ivar->type; }// 获取属性的偏移量 ptrdiff_t ivar_getOffset(Ivar ivar) { if (!ivar) return 0; return *ivar->offset; }

property_t相较,Ivar获取相关属性就简单许多,直接获取了相关的名称,属性等,但是,获取类型的方法,相较property_t只返回了变量的类型,并没有返回变量的相关的修饰符,如strongweak等。
class_copyIvarList 接下来我们看获取类中的变量列表。
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; }

从上述代码可以看出ivars的存储结构是cls->data()->ro->ivars,然后也是和property_t一样分配空间给result,最后拼接成返回结果。
object_setIvar和object_setIvarWithStrongDefault 我们可以通过object_setIvarobject_setIvarWithStrongDefault去修改变量的值。
// 如果内存管理方式没有设置,使用weak void object_setIvar(id obj, Ivar ivar, id value) { return _object_setIvar(obj, ivar, value, false /*not strong default*/); } // 如果内存管理方式没有设置,使用strong void object_setIvarWithStrongDefault(id obj, Ivar ivar, id value) { return _object_setIvar(obj, ivar, value, true /*strong default*/); }// `object_setIvar`和`object_setIvarWithStrongDefault`最后都回使用`_object_setIvar`去设置值。 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; // 查找ivar的offset,及内存存储方式。 _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement); if (memoryManagement == objc_ivar_memoryUnknown) { if (assumeStrong) memoryManagement = objc_ivar_memoryStrong; else memoryManagement = objc_ivar_memoryUnretained; }//计算ivar的内存位置 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"); } }

为示例对象赋值的方法还有object_setInstanceVariableobject_setInstanceVariableWithStrongDefault,这两个方法可以为实例变量赋值。
object_getIvar 我们可以通过object_getIvar获取实例对象的变量值。
id object_getIvar(id obj, Ivar ivar) { if (!obj||!ivar||obj->isTaggedPointer()) return nil; ptrdiff_t offset; objc_ivar_memory_management_t memoryManagement; // 查找ivar的offset,及内存存储方式。 _class_lookUpIvar(obj->ISA(), ivar, offset, memoryManagement); //计算ivar的内存位置 id *location = (id *)((char *)obj + offset); // 根据内存管理方式返回对应的值 if (memoryManagement == objc_ivar_memoryWeak) { return objc_loadWeak(location); } else { return *location; } }

class_addIvar 我们也可以通过class_addIvar为类添加一个成员变量
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 hereivar_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 = (1count++); #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相关属性 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; }

总结 ivar也可以满足我们添加和获取变量,那和property_t有什么区别呢?
property包含了ivargettersetter方法,也就是说我们添加一个property会直接为我们增加gettersetter的方法,而ivar只会增加属性。
所以,我们在大多数时候都会选择property而不是ivar
【ObjC|ObjC 学习笔记(四)(ivar)】更好的阅读体验可以参考个人网站:https://zevwings.com

    推荐阅读