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 一共三大步骤: 消息发送流程: 涉及方法列表和方法缓存 动态方法解析流程: 消息转发流程: