runtime语言-分类为什么不生成setter和getter

前言

前日有人问我一个题目:为何分类不可能半自动创设get
set方法。老实说,作者向来不曾去思考过那些题材。于是本次通过代码实践跟runtime源码来探索这些题材。

Mou
MacDown
是我在 Mac 下用过的七款可以的 马克(Mark)down 编辑器。之前一向利用的是
Mou,但不知怎的近年 Mou 在保存时总有 4s
以上的卡顿,那让我很不爽,没找到有效的化解方法,于是我被迫去找寻其余的
MD 编辑器。我尝试过很多种,但总觉得没有 Mou 体验好,最后让自家找到了
MacDown——OS X下开放源代码 马克down 编辑器。

未雨绸缪工作

为了能减小输出类数据的代码工作,作者依据NSObject的分类封装了一套代码

里头输出类实例变量的现实代码:

  • (void)logIvarsWithExpReg: (NSString *)expReg customed: (BOOL)customed
    {
    [NSObject kRecordOBJ];
    unsigned int ivarCount;
    Ivar * ivars = class_copyIvarList([self class], &ivarCount);
    for (int idx = 0; idx < ivarCount; idx++) {
    Ivar ivar = ivars[idx];
    NSString * ivarName = [NSString stringWithUTF8String:
    ivar_getName(ivar)];
    if (customed && [kOBJIvarNames containsObject: ivarName]) {
    continue;
    }
    if (expReg && !kValidExpReg(ivarName, expReg)) {
    continue;
    }
    printf(“ivar: %s — %s\n”, NSStringFromClass([self
    class]).UTF8String, ivarName.UTF8String);
    }
    free(ivars);
    }
    +(void)kRecordOBJ采用dispatch_once的办法将NSObject留存的数量存储到多少个数组中,用来扫除父类的数目输出

悄悄的故事

很有趣味关切了那四款软件的小编及私下的故事,发现很有意思,在测评七款软件从前我们先八一八故事吧。

Mou
的小编罗晨,个人主页:http://chenluois.com/,现居住塞尔维亚Bell格莱德,自由职业者。MacDown
的小编Tzu-ping
Chung,个人主页:https://uranusjr.com/,现居住新德里市,应该是江西同胞吧。

根据 MacDown
作者的介绍,他早已一度是
马克(Mark)down 的重度用户,而使用的编辑器基本是 Mou,但 Mou 可以处理fenced
code
blocks
,却对代码高亮不协助,同时在渲染
马克(Mark)down 时也有 bug,那让他很干扰。Mou
的小编当时正准备转手该软件,从来未曾立异,所以,他就开首从头早先模仿
Mou 写一个,因为是 马克down editor for Macs,所以取名为 MacDown。

MacDown 小编 Chung 在征求 Mou 小编 Luo 的允许使用了 Mou
的六款大旨,公布了 MacDown 的本来版本。Luo 最终发现 MacDown
时,很气愤,并斥责 Chung 是 copycat,意思是 MacDown 山寨了 Mou。Chung
也发现到确实是自己抄袭了 Mou
很多东西,依照某条推文的建议(并不是Luo发布的),将之前
github
类型描述
改成了:

MacDown is an open source Markdown editor for OS X, released under the
MIT License. The author stole the idea from Chen Luo’s Mou so that
people can make crappy clones.
^1

正如详细的情节能够瞻仰 Chung
博客。至于
MacDown 和 Mou 的关联是哪些的,是或不是 MacDown 就是不道德地克隆了 Mou
呢?这几个每个人都有协调的见识,那里就不研究了。

愉悦的是,方今五款软件都找到自己的升华情势,Mou 已经到位了众筹,即将发表1.0 版本,如若有对 Mou
有情怀的同窗可以支持小编;MacDown
照旧会走自己开源的征程。

Chung 的一句话也道出了我的心声:

Let’s focus on making better software for everyone.

好了,八卦完了,最后我要对两位作者表示真心的谢意,贡献给大家好用的软件!上面我会根据自身感受,分别提一下七款软件各自的特征地方。

类的习性

  • 好端端创造类
    @interface Person: NSObject {
    int _pId;
    }

    @property (nonatomic, copy) NSString * name;
    @property (nonatomic, assign) NSUInteger age;
    
    @end
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            Person * p = [[Person alloc] init];
            [p logCustomIvars];
            [p logCustomMethods];
            [p logCustomProperties];
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    

运转结果:属性nameage转移了对应的_propertyName的实例变量以及settergetter

  • 动态生成属性age
    @implementation Person
    @dynamic age;

    @end
    

运作结果:缺乏了_age变量以及对应的setAge:age方法

  • 手动已毕setter/getter
    @implemetation Person
    @dynamic age;

    - (void)setAge: (NSUInteger)age {}
    - (NSUInteger)age { return 18; }
    
    @end
    

出口结果:未变动_age实例变量

  • 手动完结_pIdsetter/getter
    @implemetation Person
    @dynamic age;

    - (void)setAge: (NSUInteger)age {}
    - (NSUInteger)age { return 18; }
    
    - (void)setPId: (int)pId { _pId = pId; }
    - (int)pId { return _pId; }      
    
    @end
    
    [p setValueForKey: @"pId"];
    

运行结果:KVC的走访会触发setter方法,_pId除开不可以透过点语法访问外,其余表现与@property无异

透过下面的几段试验,能够汲取@property的公式:

一同作用

  • 提供丰硕的简单大方美观的大旨,同时帮助自定义
  • 提供丰硕的渲染 马克down 之后的 CSS 样式,同时协助自定义样式
  • 英文单词的自行补全成效,按下 Esc 键列出补全的列表
  • 字符、单词统计效用
  • 支持 fenced code blocks
  • TeX 数学公式的匡助
  • 支撑导出 HTML 和 PDF 二种格式
  • 便利的火速键操作

分拣属性

  • 分类中添加weighheight属性
    @interface Person (category)

    @property (nonatomic, assign) CGFloat weigh;
    @property (nonatomic, assign) CGFloat height;
    
    @end
    

运作结果:weighheight未生成实例变量以及相应的setter/getter,与@dynamic修饰的age呈现同样

  • 使用@synthesize自动合成setter/getter主意时编译报错

  • 手动落成setter/getter
    @implemetation Person (category)

    - (void)setWeigh: (CGFloat)weigh {}
    - (CGFloat)weigh { return 150; }
    
    @end
    

运作结果:与@dynamic age后重写其setter/getter表现一样

  • 动态绑定属性来兑现setter/getter
    void * kHeightKey = &kHeightKey;
    @implemetation Person (category)

    - (void)setWeigh: (CGFloat)weigh {}
    - (CGFloat)weigh { return 150; }
    
    - (void)setHeight: (CGFloat)height {
        objc_setAssociatedObject(self, kHeightKey, @(height), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    - (CGFloat)height { 
        return return [objc_getAssociatedObject(self, kHeightKey) doubleValue];;
    }
    
    @end
    
    [p logCustomIvars]
    [p logCustomMethods];
    [p logCustomProperties];
    
    CGFloat height = 180;
    p.height = 180;
    height = p.height;
    
    [p logCustomIvars]
    [p logCustomMethods];
    [p logCustomProperties];
    

运行结果:动态绑定前后ivar尚无发出其他变动

通过代码实验,可以汲取上边五个结论:

  • 分拣属性相当于@dynamic property
  • 缺少ivar的情事下不可以利用@synthesize自动合成属性

以及一个预计:

  • 在类成就加载后不能够继续增加ivar

通过runtime动态创建类验证预计:

int main(int argc, char * argv[]) {

    NSString * className = @"Custom";
    Class customClass = objc_allocateClassPair([NSObject class], className.UTF8String, 0);
    class_addIvar(customClass, @"ivar1".UTF8String, sizeof(NSString *), 0, "@");
    objc_property_attribute_t type1 = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership1 = { "C", "N" };
    objc_property_attribute_t atts1[] = { type1, ownership1 };
    class_addProperty(customClass, "property1", atts1, 2);

    objc_registerClassPair(customClass);
    id instance = [[customClass alloc] init];
    NSLog(@"\nLog Ivars ===================");
    [instance logCustomIvars];
    NSLog(@"\nLog methods ===================");
    [instance logCustomMethods];
    NSLog(@"\nLog properties ===================");
    [instance logCustomProperties];

    class_addIvar(customClass, @"ivar2".UTF8String, sizeof(NSString *), 0, "@");
    objc_property_attribute_t type2 = { "T", "@\"NSString\"" };
    objc_property_attribute_t ownership2 = { "C", "N" };
    objc_property_attribute_t atts2[] = { type2, ownership2 };
    class_addProperty(customClass, "property2", atts2, 2);
    instance = [[customClass alloc] init];
    NSLog(@"\nLog Ivars ===================");
    [instance logCustomIvars];
    NSLog(@"\nLog methods ===================");
    [instance logCustomMethods];
    NSLog(@"\nLog properties ===================");
    [instance logCustomProperties];
}

运行结果:在调用class_registerClassPair后,添加ivar失败

Mou 特色功用

从源码解析

objc_class的协会体定义如下:

struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
    // CLS_EXT only
    const uint8_t *ivar_layout;
    struct old_class_ext *ext;
}

ps: 在新本子中结构体内部已经发生了大改,可是其中的属性大约上仍是那些

那里面有个重要的习性ivar_layout,顾名思义存放的是变量的职位属性,与之相应的还有一个weakIvarLayout变量,不过在默许结构中从不出现。那七个属性用来记录ivar哪些是strong或者weak,而这么些记录操作在runtime等级已经被确定好。正由于那样,那极有可能是ivar手足无措在类被加载后继续增加的因由之一。ivar_layout的更加多通晓可以参见Objective-C
Class Ivar
layout
一文

import操作支持编译检查和链接进程,可是在category的加载进程中,不会将增添的内容添加到原始的类社团中。runtime对于category的加载进度可以大约的分成下边几步(摘自objc
category的密码
):

  • objc runtime的加载入口是一个叫_objc_init的方法,在library加载前由libSystem dyld调用,举行早先化操作
  • 调用map_images方法将文件中的image map到内存
  • 调用_read_images主意起头化map后的image,这些中干了过多的事情,像load不无的类、协议和category,著名的+ load主意就是这一步调用的
    -仔细看category的开始化,循环调用了_getObjc2CategoryList格局,这一个法子拿出来看看:
  • .…

那整个的历程爆发在_objc_init函数中,函数完结如下

简短来说在load_images函数中最终会走到下边的代码调用来加载所有的类以及类的分类

依照上边的代码加上runtime的加载顺序,可以继承生产:

  • @dynamic其实是将性能的加载推迟到类加载成功后

别的,前面也说过在缺少ivar的情事下不可以自动合成setter/getter,除了category本人是不被添加到类社团中的,所以无法使用类社团的ivar合成属性外,还有分类自身结构的题目

struct category_t {
    const char *name;    ///  类名
    classref_t cls;  ///  类指针
    struct method_list_t *instanceMethods;  ///  实例方法
    struct method_list_t *classMethods;  ///  类方法
    struct protocol_list_t *protocols;  ///  扩展的协议
    struct property_list_t *instanceProperties;  ///  扩展属性

    method_list_t *methodsForMeta(bool isMeta) { ... }
    property_list_t *propertiesForMeta(bool isMeta) { ... }
};

可以看来分类布局本身是不存在ivar的器皿的,由此紧缺了自动合成属性的规格。最终还有一个问题,我们在利用objc_associate多重函数绑定属性的时候那些变量存储在了什么地方?

集成 Tumblr 和 Scriptogr.am 公布博文

可以配备 Tumblr 和 Scriptogr.am 的信箱,在 Mou 上写小说通过
File->Post-> 可以发布到那多个阳台上,格外有利。

Mou 援助集成 Tumblr 和 Scriptor.am

总结

第一,iOS的分类在runtime兑现的结构体中并不存在Ivar项目的器皿,缺乏了自动合成setter以及getter的必要条件,因而在分拣中宣称的属性默认为@dynamic修饰。

说不上,OC本身是一门原型语言,对象和类原型很像。类对象实施alloc办法就像原型情势中的copy操作一样,类保存了copy所需的实例消息,这几个音信内存新闻在runtime加载时就被固定了,没有增添Ivar的条件。(感谢大表哥的科普)

最后,在runtime中存在一个品类为AssociationHashMap的哈希映射表保存着对象动态增进的习性,每个对象以自我地址为key有限扶助着一个绑定属性表,我们动态拉长的性质就都存储在那么些表里,那也是动态增进property能不负众望的功底。

上一篇:闲话内存管理

转发请评释原文地址及小编

言语协理

Mou 内置 CJK
字符协助,你可以很不难选拔中文、日文、葡萄牙语等来写文章,同时还扶助竖排的品格。

Mou 支持 CJK 及竖排

MacDown 特色

代码高亮

Mou 和 MacDown 都协理 fenced code
blocks(前后多个反引号可以代表代码块),但 MacDown
接济加语言标识符落成代码高亮,那对程序员来说几乎是福音啊,分外棒的法力。

MacDown 辅助代码高亮

GFM Task List 支持

MacDown 支持 Task
list
,有了这些效应,你可以将你的
MD 编辑器立马变成 TODO list,是或不是很赞?

MacDown 对 Task list 的支持

Jekyll Front-matter 支持

过几人使用 Jekyll 作为博客引擎,那时 Jekyll
的先头那段该怎么去渲染呢?MacDown 和 github 一样可以扶助。

MacDown 对 Jekyll front-matter 的支持

总结

OK,统计完了,要是或不是 Mou 在我的 Mac
上有卡顿,我也没想着要换一款编辑器,近年来自己在用 MacDown
感觉依然很不利的,因为有代码高亮接济就可以把自己留给了。至于你选哪一款,你可以下载下来自己经验体验,反正软件都很小。也许你跟我同样,因为某个小缺点甩掉某个软件,又因为某个小成效的大悲大喜而喜欢上某个软件。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图