死磕Objective-C runtime运行时之一

数字拓荒客
• 阅读 3383

说到Objc运行时,如果你还不清楚,那可要看仔细了,如果你是靠颜值而不是才华能够顺利通过面试,喵了个咪的,我也想去试试

Objc运行时2.0

iOS出现时就是运行时2.0版本了,和旧的相比拥有两大特性:
第一,就是修改,增加或删除一个类的实例变量,不用再重新编译子类就可以用了。
第二,就是加为@property加入了Synthesize

实战问题一(答案在最后):

请将属性property(atomic,nonatomic, assign, weak, strong, copy, readonly, readwrite blah!blah!)按照功能分组

实战问题二(答案在最后):

请回答下列@property变量cool和cat的默认属性

@interface ViewController : UIViewController

@property BOOL cool;
@property NSObject *cat;

@end

Objc运行时

Objc运行时相当于Objective-C的操作系统,当我们编译代码时,编译器把源码编译成运行时能够懂得数据结构(比如isa包括类的继承信息)和运行时方法(比如objc_msgSend), 剩下的就交给运行时动态处理了。

NSObject成员变量isa

@interface NSObject{
    objc_class *isa
}

//点开isa链接搜索struct objc_class : objc_object能够查到其声明
struct objc_class : objc_object {
    // Class ISA;
    objc_class *superclass;
    ....
    方法派遣表   selector对应的C语言函数
    ....
}

从objc_class声明可以看出,每一个isa->superclass同样是objc_class类型,
这样就组成了一个继承的链表结构

运行时3种使用方式

1.写Objective-C代码必然用到,虽然没这种感觉
2.调用NSObject运行时方法
3.直接调用运行时API

运行时objc_msgSend

objc方法只不过是C语言方方法,加上两个特殊的参数第一个是receiver(self),第二个参数是selector(_cmd)

比如以下的方法调用
[receiver message:arg1] //假设定义arg1为BOOL类型
具体实现:
(void (*)(id, SEL, BOOL))[target methodForSelector:@selector(message:)];
运行时:

`objc_msgSend(receiver, selector, arg1)`

objc_msgSend做的事情如下:

  1. 根据receiver和selector先在receiver的方法派遣表里面查找是否有selector这个方法实现, 如果没找到,receiver->isa->superclass去查找,以此类推,直到找到对应的方法实现,若直到NSObject都没有找到对应实现,中间过程在下文解释,最后掉用[receiver doesNotRecognizeSelector:_cmd]就抛异常出错了

  2. 将所有参数传给具体实现方法调用

  3. 将具体实现结果反回来

跳过运行时objc_msgSend

尽管objc_msgSend在成功调用一次方法之后,每个Class会有缓存,下次重复调用该方法时,查找速度会大大提高,但是还是有运行时消息发送的时间,如果一个函数被调用成千上万次,为了减少消息派遣时间,可以跳过运行时objc_msgSend,直接调用方法实现

void (*message)(id, SEL, BOOL);
int i;
 
message = (void (*)(id, SEL, BOOL))[target
    methodForSelector:@selector(message:)];
for ( i = 0 ; i < 1000 ; i++ )
    setter(targetList[i], @selector(message:), YES);

问题一答案:

原子性:atomic,nonatomic
读写性: readwrite, readonly
ARC版内存管理: assign, strong, copy, weak

问题二答案:

@property cool = TB,V_cool
@property cat = T@"NSObject",&,V_cat

子问题1: 你是怎么得到答案的?答案就是用运行时啦!!!

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    
    id LenderClass = objc_getClass("ViewController");
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(LenderClass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        fprintf(stdout, "@property %s = %s\n", property_getName(property), property_getAttributes(property));
    }
}

子问题2: 如何解读?首先说明这奇怪的字符时编译器干的好事啦,参考主要是这里还有这里

The string returned by |property_getAttributes| starts with a T followed by the @encode type and a comma, and finishes with a V followed by the name of the backing instance variable.

上面一坨就是说,property_getAttributes获得的字符串的格式,以T开头,然后是<变量类型>,然后是一个逗号,然后是<属性>,最后是一个V,再往后就是下划线_和变量名了


/*根据文档:
如果是readonly应该有R属性, 所以默认应该是readwrite
如果是weak,strong,copy应该分别有W, &, C, 所以默认应该是assign
若果有nonatomic应该有N, 所以默认是atomic       

TB,V_cool B代表BOOL, 中间什么属性都没有对不对,根据以上分析,默认是atomic, assign, readwrite 
T@"NSObject",&,V_cat 则是atomic, strong, readwrite
*/
点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
4年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
Stella981 Stella981
4年前
PhoneGap设置Icon
参考:http://cordova.apache.org/docs/en/latest/config\_ref/images.html通过config.xml中的<icon标签来设置Icon<iconsrc"res/ios/icon.png"platform"ios"width"57"height"57"densi
Wesley13 Wesley13
4年前
MySQL总结(十一)子查询
!(https://oscimg.oschina.net/oscnet/upa344f41e81d3568e3310b5da00c57ced8ea.png)子查询1\.什么是子查询需求:查询开发部中有哪些员工selectfromemp;通
Easter79 Easter79
4年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Python进阶者 Python进阶者
2年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
数字拓荒客
数字拓荒客
Lv1
愿你所在的城市,有暖阳,有清风,每天都过得很好。
文章
5
粉丝
0
获赞
0