iOS基础概念3
函数编码类型-定义
编码 含义
c A char
i An int
s A short
l A long
l is treated as a 32-bit quantity on 64-bit programs.
q A long long
C An unsigned char
I An unsigned int
S An unsigned short
L An unsigned long
Q An unsigned long long
f A float
d A double
B A C++ bool or a C99 _Bool
v A void
* A character string (char *)
@ An object (whether statically typed or typed id)
# A class object (Class)
: A method selector (SEL)
[array type] An array
{name=type...} A structure
(name=type...) A union
bnum A bit field of num bits
^type A pointer to type
? An unknown type (among other things, this code is used for function
pointers)
iOS提供了一个@encode指令,可以将具体的类型表示成字符串编码, 看下面代
码:
NSLog(@"%s",@encode(int));
NSLog(@"%s",@encode(float));
NSLog(@"%s",@encode(int *));
NSLog(@"%s",@encode(id));
NSLog(@"%s",@encode(void));
NSLog(@"%s",@encode(SEL));
NSLog(@"%s",@encode(float *));
打印结果: i
打印结果: f
打印结果: ^i
打印结果: @
打印结果: v
打印结果: :
打印结果: ^f
----
函数编码-举例
知识点:OC方法对应的底层函数前两个是默认参数id self 和SEL cmd
实例详解:
- (int)test:(int)age height:(float)height
方法从左到右,返回值和参数的类型分别为:
int->id->SEL->int->float
转换成类型编码,我猜想应该是:
i-@-:-i-f
而最终系统是这样表示的:
i24@0:8i16f20
详解
i —— 函数的返回值类型为int
24 —— 此方法所有参数所占的总长度(24字节)
@ —— 第一个参数id
0 —— 第一个参数在内存中的起始偏移量(0字节,也就是从第0个字节开始算
起)
: —— 第二个参数SEL
8 —— 第二个参数在内存中的起始偏移量(8字节,也就是从第8个字节开始算
起,因此上面的id参数占之前的8个字节)
i —— 第三个参数int
16 —— 第三个参数在内存中的起始偏移量(16字节,也就是从第16个字节开始
算起,因此上面的SEL参数占用了之前的8个字节)
f —— 第四个参数float
20 —— 第四个参数在内存中的起始偏移量(20字节,也就是从第20个字节开始
算起,因此上面的int参数占用了前面的4个字节,而总长度为24,因此最后的4
个字节是给float参数用的)
Class方法缓存:cache_t
分源码详解篇1,总结篇2
cache_t 篇1: 源码
回顾class结构:
struct objc_class : objc_object { // class结构体
// Class ISA;
Class superclass;
cache_t cache; // 方法缓存
class_data_bits_t bits;
};
cache_t cache
是散列表
struct cache_t {
struct bucket_t *_buckets; //缓存方法的散列/哈希表
mask_t _mask; // == 散列表长度 - 1
mask_t _occupied; // 已缓存的方法的数量
}
bucket_t
方法缓存单元
struct bucket_t {
private:
cache_key_t _key; // 方法的SEL,也就是方法名
IMP _imp; // 函数的内存地址
}
类似理解就是cache_t 里有字典dictiony,里面存着 bucket_t ,方法调用时根据
方法名称key 取 IMP
列表中查询:
排序的话就是二分查找,否则就会遍历
列表插入:
调用方法没有缓存过,就会添加到调用者读类对象缓存列表
扩容: 当列表达到3/4时,成倍 *2 扩容,并且原来旧列表缓存过的方法全部被
删除
下次再次调用的话,有需要被重新缓存了
源码逻辑:
lookUpImpOrForward 方法查找函数, ️ ️ ️处标记
code
IMP lookUpImpOrForward(Class cls, SEL sel, id inst,
bool initialize, bool cache, bool resolver) {
IMP imp = nil;
bool triedResolver = NO;
runtimeLock.assertUnlocked();
// 【Runtime 第一步:消息发送流程】
//-> ️ ️ ️查询当前Class对象的缓存,如果找到方法,就返回该方法
if (cache) {
imp = cache_getImp(cls, sel);
if (imp) return imp;
}
runtimeLock.read();
//--> ️ ️ ️当前Class如果没有被realized,就进行realize操作
if (!cls->isRealized()) {
runtimeLock.unlockRead();
runtimeLock.write();
realizeClass(cls);
runtimeLock.unlockWrite();
runtimeLock.read();
}
//--> ️ ️ ️当前Class如果没有初始化,就进行初始化操作
if (initialize && !cls->isInitialized()) {
runtimeLock.unlockRead();
_class_initialize (_class_getNonMetaClass(cls, inst));
runtimeLock.read();
}
retry:
runtimeLock.assertReading();
//--> ️ ️ ️尝试从该Class对象的缓存中查找,如果找到,就跳到done处返
回该方法
imp = cache_getImp(cls, sel);
if (imp) goto done;
//--> ️ ️ ️尝试从该Class对象的方法列表中查找,找到的话,就缓存到该
Class的cache_t里面,并跳到done处返回该方法
{
Method meth = getMethodNoSuper_nolock(cls, sel);
if (meth) {
log_and_fill_cache(cls, meth->imp, sel, inst, cls);
imp = meth->imp;
goto done;
}
}
//--> ️ ️ ️进入当前Class对象的superclass对象
{
unsigned attempts = unreasonableClassCount();
for (Class curClass = cls->superclass;//---> ️ ️ ️该for循环每循环
一次,就会进入上一层的superclass对象,进行循环内部方法查询流程
curClass != nil;
curClass = curClass->superclass)
{
// Halt if there is a cycle in the superclass chain.
if (--attempts == 0) {
_objc_fatal("Memory corruption in class list.");
}
// Superclass cache.------> ️ ️ ️在当前superclass对象的缓存进
行查找
imp = cache_getImp(curClass, sel);
if (imp) {
if (imp != (IMP)_objc_msgForward_impcache) {
// Found the method in a superclass. Cache it in this class.
log_and_fill_cache(cls, imp, sel, inst, curClass);
goto done;//------> ️ ️ ️如果在当前superclass的缓存里找
到了方法,就调用log_and_fill_cache进行方法缓存,注意这里传入的参数是
cls,也就是将方法缓存到消息接受对象所对应的Class对象的cache_t中,然后跳
到done处返回该方法
}
else {
// Found a forward:: entry in a superclass.
// Stop searching, but don't cache yet; call method
// resolver for this class first.
break;//----> ️ ️ ️如果缓存里找到的方法是
_objc_msgForward_impcache,就跳出该轮循环,进入上一层的superclass,
再次进行查找
}
}
// Superclass method list.----> ️ ️ ️如过画缓存里面没有找到方
法,则对当前superclass的方法列表进行查找
Method meth = getMethodNoSuper_nolock(curClass, sel);
if (meth) {
//------> ️ ️ ️如果在当前superclass的方法列表里找到了方法,就
调用log_and_fill_cache进行方法缓存,注意这里传入的参数是cls,也就是将方
法缓存到消息接受对象所对应的Class对象的cache_t中,然后跳到done处返回
该方法
log_and_fill_cache(cls, meth->imp, sel, inst, curClass);
imp = meth->imp;
goto done;
}
}
}
// No implementation found. Try method resolver once.
//---> ️ ️ ️ 【Runtime 第二步】: 如果到基类还没有找到方法,就尝试
进行方法解析
if (resolver && !triedResolver) {
runtimeLock.unlockRead();
_class_resolveMethod(cls, sel, inst);
runtimeLock.read();
// Don't cache the result; we don't hold the lock so it may have
// changed already. Re-do the search from scratch instead.
triedResolver = YES;
goto retry;
}
// No implementation found, and method resolver didn't help.
//------> ️ ️ ️ 【Runtime 第三步】:如果方法解析不成功,就进行消息
转发
// Use forwarding.
imp = (IMP)_objc_msgForward_impcache;
cache_fill(cls, sel, imp, inst);
done:
runtimeLock.unlockRead();
return imp;
}
cache_t 篇2: runtime ,放到下面runtime部分讲解
Runtime 一共三大步骤:
消息发送流程: 涉及方法列表和方法缓存
动态方法解析流程:
消息转发流程: