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
只返回了变量的类型,并没有返回变量的相关的修饰符,如strong
,weak
等。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_setIvar
和object_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_setInstanceVariable
和object_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
包含了ivar
及getter
和setter
方法,也就是说我们添加一个property
会直接为我们增加getter
和setter
的方法,而ivar
只会增加属性。所以,我们在大多数时候都会选择
property
而不是ivar
。【ObjC|ObjC 学习笔记(四)(ivar)】更好的阅读体验可以参考个人网站:https://zevwings.com
推荐阅读
- EffectiveObjective-C2.0|EffectiveObjective-C2.0 笔记 - 第二部分
- 由浅入深理解AOP
- 继续努力,自主学习家庭Day135(20181015)
- python学习之|python学习之 实现QQ自动发送消息
- Android中的AES加密-下
- 一起来学习C语言的字符串转换函数
- 定制一套英文学习方案
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- 《深度倾听》第5天──「RIA学习力」便签输出第16期
- 如何更好的去学习