AG亚游集团 >iOS开发

iOS动态添加属性

2019-01-07 11:25 编辑: 米米狗 分类:iOS开发 来源:投稿

背景

AG亚游集团,王声威表示,营运初期是创造运量的关键时期,若真的以现有票价开通,未来一定会衍生运量不足问题,将陷机场地铁财务于窘境。他说,桃园地铁公司目前预估,机场地铁的日运量约4.6万人次,“但往来双北的307公交车,日运量就达7万人次,更不用说日运量200万人次的台北地铁、37万的台铁台北中坜区间车”。

  此外,我注意到在支持机构之中,有法国企业创新网、法中委员会和法国顾问们。有什么好的发展项目普及国粹艺术,推广民族经典,传承优秀文化,培育家国情怀是垂杨柳教辅中心特色办学、立德树人的突破口。其推出的国粹文化“国戏国乐”板块学生大课堂活动课程,每年吸引万余名青少年学生体验民族戏曲艺术的魅力,促进经典文化更好地传承发展。

之前一篇文章《iOS关联对象》详细介绍了如何通过关联对象添加属性,本篇文章将介绍如何通过runtime的class_addPropertyclass_addIvar动态添加属性,并且带领大家看看这两个方法底层是如何实现的。

class_addProperty添加属性

对于已经存在的类我们用class_addProperty方法来添加属性,而对于动态创建的类我们通过class_addIvar添加属性, 它会改变一个已有类的内存布局,一般是通过objc_allocateClassPair动态创建一个class,才能调用class_addIvar创建Ivar,最后通过objc_registerClassPair注册class。

对于已经存在的类,class_addIvar是不能够添加属性的

首先我们声明了一个Person

@interface Person : NSObject
@property (nonatomic,copy)NSString *name;
@end

然后我们通过class_addProperty动态添加属性

id getter(id object,SEL _cmd1){
    NSString *key = NSStringFromSelector(_cmd1);
    return objc_getAssociatedObject(object, (__bridge const void * _Nonnull)(key));
}
void setter(id object,SEL _cmd1,id newValue){
    NSString *key = NSStringFromSelector(_cmd1);
    key = [[key substringWithRange:NSMakeRange(3, key.length-4)] lowercaseString];
    objc_setAssociatedObject(object, (__bridge const void * _Nonnull)(key), newValue, OBJC_ASSOCIATION_RETAIN);
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@"%@"",NSStringFromClass([NSString class])] UTF8String] }; /type
        objc_property_attribute_t ownership0 = { "C""" }; / C = copy
        objc_property_attribute_t ownership = { "N""" }; /N = nonatomic
        objc_property_attribute_t backingivar  = { "V", [[NSString stringWithFormat:@"_%@", @"sex"] UTF8String] };  /variable name
        objc_property_attribute_t attrs[] = { type, ownership0, ownership,backingivar};/这个数组一定要按照此顺序才行
        BOOL add = class_addProperty([Person class], "sex", attrs, 4);
        if (add) {
            NSLog(@"添加成功
"
);
        }else{
            NSLog(@"添加失败
"
);
        }

        class_addMethod([Person class], NSSelectorFromString(@"sex"), (IMP)getter, "@@:");
        class_addMethod([Person class], NSSelectorFromString(@"setSex:"), (IMP)setter, "v@:@");

        unsigned int count;
        objc_property_t *properties =class_copyPropertyList([Person class], &count);
        for (int i = 0; i < count; i++) {
            objc_property_t property = properties[i];
            NSLog(@"名字:%s--属性:%s
"
,property_getName(property),property_getAttributes(property));
        }

        Person *person = [Person new];
        person.name = @"FlyOceanFish";
        [person setValue:@"男" forKey:@"sex"];
        NSLog(@"name:%@",person.name);
        NSLog(@"sex:%@",[person valueForKey:@"sex"]);
    }
    return 0;
}

AG亚游集团Demo地址

这里要注意几点:

  • attrs属性设置的数组一定是此顺序 此属性设置是参照原有属性的打印格式进行设置的,name是原先有的,sex是后来动态添加的,如下:

2019-01-02 14:39:39.370405+0800 MyProperty[1354:159207] 名字:sex--属性:T@"NSString",C,N,V_sex

2019-01-02 14:39:39.370425+0800 MyProperty[1354:159207] 名字:name--属性:T@"NSString",C,N,V_name

  • 添加完属性我们要添加上对应的set、get方法,因为我们是通过kvo的方式设值和取值的,它会调用set、get方法,如果没有的话,会报错。

底层代码实现

上边代码演示了如何动态添加属性,接下来让我们看看苹果底层是如何实现的。

class_addProperty

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);
}


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
        rwlock_writer_t lock(runtimeLock);
        try_free(prop->attributes);
        prop->attributes = copyPropertyAttributeString(attrs, count);
        return YES;
    }
    else {
        rwlock_writer_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;
    }
}

通过代码我们可以看到如果添加一个已经存在的属性是添加不成功的; 添加一个新属性,是实例化了一个property_list_t对象,最终调用了cls的data方法,返回了class_rw_t指针,最终添加在属性properties的一个数组中。

还有一个结构体的名字是class_ro_t,与class_rw_t是配合使用的,大家有兴趣可以自行去研究。ro即read only;rw即read write。看到这里应该能猜个八九不离十

class对象结构体

struct objc_class : objc_object {
    / Class ISA;
    Class superclass;
    cache_t cache;             / formerly cache pointer and vtable
    class_data_bits_t bits;    / class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() {
        return bits.data();
    }
    ...
  }

class_rw_t结构体

struct class_rw_t {
    / Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    ...
  }

class_addIvar

BOOL

{
    if (!cls) return NO;

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

    rwlock_writer_t lock(runtimeLock);

    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 = (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.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_t对象,并且存储在了class_ro_t对象中了。所以跟我们上边说的改变了类的内存布局一致。

总结

我们通过一个demo实现了动态添加属性,通过底层源码解析让大家彻底认识了class_addPropertyclass_addIvar两个方法。runtime让oc成为了一门动态语言,只有我们想不到的,没有runtime做不到的。

我的博客

AG亚游集团FlyOceanFish


搜索CocoaChina微信公众号:CocoaChina
微信扫一扫
订阅每日移动开发及APP推广热点资讯
公众号:
CocoaChina
上一篇:2018全年复盘总结
下一篇:Swift修饰符(final、override、discardableResult、mutating、lazy、inou
我来说两句
发表评论
您还没有登录!请登录注册
所有评论(0

综合评论

相关帖子

sina weixin mail 回到顶部
联想与富士通合资公司开发AI设备:能读懂表情 女子高速路上停车 只为让孩子连网上英语课 马里中资公司项目受袭 中方人员吊车被烧电脑被抢 协会年度赛工作会开幕 陈泽兰:谋划象棋新局面 朝鲜“美国通”升任外务省副相 深得金正恩信任 央视315曝光季节性谣言:仿真包菜?注射西瓜?假的 中兴再回应美制裁:将通过合法手段维护自身权益 切尔西被英足总罚款2万英镑 球员抗议裁判惹祸 央视:上港亚冠有这状态不会输 权健没这人真不行 日本担忧贸易战劝美\"开小灶\":贡献大应得到豁免 5G进入规模外场试验阶段 万亿投资惠及中国厂商 从中国到欧洲 原来还可以这么走
伊拉克大选非当权派领先 现任总理阵营不容乐观 苹果向印度政府提出替代方案 避免iPhone被退网 汽车之家第四季度净利润1.192亿美元 同比增长74% 澳官员抛中国援助无用论 太平洋岛国集体发声驳斥 央行:个人住房贷款虽有减少 但仍是较快增长 规模罕见 泰国1.1万家7-11便利店将采用人脸识别技… 陕西汉中12岁女中学生在校意外死亡 警方展开调查 甘肃多地再次遭遇短时强降雨 部分城区内涝 亚冠-水原三星主场3-0蔚山现代 总分3-1晋级八强 蔡奇:全力应对汛期台风叠加影响 确保万无一失 999名罪犯春节离监探亲自行回监:多地曾停止执行 台当局人事变换如“大风吹” 台教授:问题在首脑
北京中小学生观战WCBA总决赛 赛场成第二课堂 摩拜的投资人:公司能被收购,也是了不起的伟大成就 伊方:如伊核协议规定利益得不到保障 将另做打算 首艘国产航母“首秀”归来 专家:达到设计指标 索尼、任天堂公布第一季度财报 主机销量均出现下滑 津媒:中韩亚冠对抗仍是主旋律 整体实力没明显差距 土耳其再炮击叙利亚库尔德武装 已连续两天 英国中部一座建筑物爆炸起火 至少5人死亡 加拿大指责中国倾销钢铁 中方:勿让别人为己挡枪 如何创业白手起家 女人开什么店比较好 白手起家的成功案例 家庭办厂项目废纸加工 AG亚游集团