YYModel底层解析- Runtime
这段时间一直在忙新的需求,没有时间来整理代码,发表自己技术博客,今天我们来看一下YYModel的底层解析以及如何使用,希望对大家有所帮助!
一 概述
概括
YYModel是一个轻量级的JSON模型转换库,它的思路非常清晰代码风格也很好,所以还是建议大家看一下底层实现的逻辑,也可以从源码加深对Runtime的理解。
简介
下面是YYModel第三方库的一些代码结构。
YYModel的总共文件只有5个文件
除掉YYModel.h之外,只剩下了YYClassInfo和NSObject+YYModel两个模块啦!
- YYClassInfo功能主要是将Runtime层级中的一些结构体封装到NSObject中调用;
- NSObject+YYModel功能是提供调用的接口以及实现具体的模型转换逻辑。
前面已经讲到YYClassInfo主要功能是将Runtime层级的结构体封装到NSObject层级以便调用。下面是YYClassInfo与Runtime层级对比:
二、详细
1.YYClassIvarInfo
YYClassIvarInfo && objc_ivar
下面是YYClassIvarInfo
- /**
- Instance variable information.
- */
- @interface YYClassIvarInfo : NSObject
- @property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
- @property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
- @property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
- @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
- @property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type
- /**
- Creates and returns an ivar info object.
- @param ivar ivar opaque struct
- @return A new object, or nil if an error occurs.
- */
- - (instancetype)initWithIvar:(Ivar)ivar;
- @end
紧接着我们看一下Runtime的objc_ivar表示变量的结构体
- struct objc_ivar {
- char * _Nullable ivar_name OBJC2_UNAVAILABLE; // 变量名称
- char * _Nullable ivar_type OBJC2_UNAVAILABLE; // 变量类型
- int ivar_offset OBJC2_UNAVAILABLE; // 变量偏移量
- #ifdef __LP64__ // 如果已定义 __LP64__ 则表示正在构建 64 位目标
- int space OBJC2_UNAVAILABLE; // 变量空间
- #endif
- }
注:日常开发中,NSString类型的属性会用copy修饰,看上面YYClassIvarInfo中typeEncoding和name是用strong修饰。这是因为其内部先是通过Runtime方法拿到const char * 之后通过 stringWithUTF8String 方法之后转为 NSString 的。所以 NSString 这类属性在确定其不会在初始化之后出现被修改的情况下,使用 strong来修饰 做一次单纯的强引用在性能上是比 copy 要高的。
YYClassMethodInfo && objc_method
下面是YYClassMethodInfo
- @interface YYClassMethodInfo : NSObject
- @property (nonatomic, assign, readonly) Method method; ///< 方法
- @property (nonatomic, strong, readonly) NSString *name; ///< 方法名称
- @property (nonatomic, assign, readonly) SEL sel; ///< 方法选择器
- @property (nonatomic, assign, readonly) IMP imp; ///< 方法实现,指向实现方法函数的函数指针
- @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 方法参数和返回类型编码
- @property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< 返回值类型编码
- @property (nullable, nonatomic, strong, readonly) NSArray<nsstring *> *argumentTypeEncodings; ///< 参数类型编码数组
- - (instancetype)initWithMethod:(Method)method;
- @end
YYClassMethodInfo则是对Rutime里面的objc_method的封装,紧接着我们看Runtime的objc_method结构体
- struct objc_method {
- SEL _Nonnull method_name OBJC2_UNAVAILABLE; // 方法名称
- char * _Nullable method_types OBJC2_UNAVAILABLE; // 方法类型
- IMP _Nonnull method_imp OBJC2_UNAVAILABLE; // 方法实现(函数指针)
- }
YYClassPropertyInfo && property_t
YYClassPropertyInfo是对Runtime中property_t的封装
- @interface YYClassPropertyInfo : NSObject
- @property (nonatomic, assign, readonly) objc_property_t property; ///< 属性
- @property (nonatomic, strong, readonly) NSString *name; ///< 属性名称
- @property (nonatomic, assign, readonly) YYEncodingType type; ///< 属性类型
- @property (nonatomic, strong, readonly) NSString *typeEncoding; ///< 属性类型编码
- @property (nonatomic, strong, readonly) NSString *ivarName; ///< 变量名称
- @property (nullable, nonatomic, assign, readonly) Class cls; ///< 类型
- @property (nullable, nonatomic, strong, readonly) NSArray<nsstring *> *protocols; ///< 属性相关协议
- @property (nonatomic, assign, readonly) SEL getter; ///< getter 方法选择器
- @property (nonatomic, assign, readonly) SEL setter; ///< setter 方法选择器
- - (instancetype)initWithProperty:(objc_property_t)property;
- @end</nsstring *>
然后来看一下Runtime的property_t结构体
- struct property_t {
- const char *name; // 名称
- const char *attributes; // 修饰
- };
YYClassInfo && objc_class
YYClassInfo封装了Runtime的objc_class,下面看一下YYClassInfo
YYClassInfo
- @interface YYClassInfo : NSObject
- @property (nonatomic, assign, readonly) Class cls; ///< 类
- @property (nullable, nonatomic, assign, readonly) Class superCls; ///< 超类
- @property (nullable, nonatomic, assign, readonly) Class metaCls; ///< 元类
- @property (nonatomic, readonly) BOOL isMeta; ///< 元类标识,自身是否为元类
- @property (nonatomic, strong, readonly) NSString *name; ///< 类名称
- @property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< 父类(超类)信息
- @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclassivarinfo *> *ivarInfos; ///< 变量信息
- @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclassmethodinfo *> *methodInfos; ///< 方法信息
- @property (nullable, nonatomic, strong, readonly) NSDictionary<nsstring *, yyclasspropertyinfo *> *propertyInfos; ///< 属性信息
- - (void)setNeedUpdate;
- - (BOOL)needUpdate;
- + (nullable instancetype)classInfoWithClass:(Class)cls;
- + (nullable instancetype)classInfoWithClassName:(NSString *)className;
- @end
objc_class
- // objc.h
- typedef struct objc_class *Class;
- // runtime.h
- struct objc_class {
- Class _Nonnull isa OBJC_ISA_AVAILABILITY; // isa 指针
- #if !__OBJC2__
- Class _Nullable super_class OBJC2_UNAVAILABLE; // 父类(超类)指针
- const char * _Nonnull name OBJC2_UNAVAILABLE; // 类名
- long version OBJC2_UNAVAILABLE; // 版本
- long info OBJC2_UNAVAILABLE; // 信息
- long instance_size OBJC2_UNAVAILABLE; // 初始尺寸
- struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE; // 变量列表
- struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // 方法列表
- struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE; // 缓存
- struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE; // 协议列表
- #endif
- } OBJC2_UNAVAILABLE;
注解:下面是Runtime关于class的知识
下面是对应的讲解。
YYClassInfo 的初始化
- + (instancetype)classInfoWithClass:(Class)cls {
- // 判空入参
- if (!cls) return nil;
- // 单例缓存 classCache 与 metaCache,对应缓存类和元类
- static CFMutableDictionaryRef classCache;
- static CFMutableDictionaryRef metaCache;
- static dispatch_once_t onceToken;
- static dispatch_semaphore_t lock;
- dispatch_once(&onceToken, ^{
- classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), , &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), , &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
- // 这里把 dispatch_semaphore 当做锁来使用(当信号量只有 1 时)
- lock = dispatch_semaphore_create();
- });
- // 初始化之前,首先会根据当前 YYClassInfo 是否为元类去对应的单例缓存中查找
- // 这里使用了上面的 dispatch_semaphore 加锁,保证单例缓存的线程安全
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
- // 如果找到了,且找到的信息需要更新的话则执行更新操作
- if (info && info->_needUpdate) {
- [info _update];
- }
- dispatch_semaphore_signal(lock);
- // 如果没找到,才会去老实初始化
- if (!info) {
- info = [[YYClassInfo alloc] initWithClass:cls];
- if (info) { // 初始化成功
- // 线程安全
- dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
- // 根据初始化信息选择向对应的类/元类缓存注入信息,key = cls,value = info
- CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
- dispatch_semaphore_signal(lock);
- }
- }
- return info;
- }
下面总结一下初始化主要步骤:
- 首先创建单例缓存,类缓存和元类缓存;
- 使用dispatch_semaphore 保证缓存线程安全;
- 初始化操作之前首先缓存中查找是否已经向缓存中注册过的当前要初始化的YYClassInfo;
- 如果查找缓存对象,需要判断对象是否需要更新以及其他相关操作;
- 如果没有找到缓存对象,就开始初始化;
- 初始化成功之后,向缓存中注册YYClassInfo实例。
2.NSObject+YYModel
NSObject+YYModel在YYModel主要任务是利用YYClassInfo层级封装的类来执行JSON模型之间的转换逻辑。下面是NSObject+YYModel讲述的主要内容:
- 类型编码的解析
- 数据结构的定义
- 递归模型的转换
- 接口相关的代码
下面将部分讲解
数据结构的定义
NSObject+YYModel重新定义了两个类,来使用 YYClassInfo 中的封装。
_YYModelPropertyMeta
- @interface _YYModelPropertyMeta : NSObject {
- @package
- NSString *_name; ///< 属性名称
- YYEncodingType _type; ///< 属性类型
- YYEncodingNSType _nsType; ///< 属性在 Foundation 框架中的类型
- BOOL _isCNumber; ///< 是否为 CNumber
- Class _cls; ///< 属性类
- Class _genericCls; ///< 属性包含的泛型类型,没有则为 nil
- SEL _getter; ///< getter
- SEL _setter; ///< setter
- BOOL _isKVCCompatible; ///< 如果可以使用 KVC 则返回 YES
- BOOL _isStructAvailableForKeyedArchiver; ///< 如果可以使用 archiver/unarchiver 归/解档则返回 YES
- BOOL _hasCustomClassFromDictionary; ///< 类/泛型自定义类型,例如需要在数组中实现不同类型的转换需要用到
- /*
- property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
- property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
- property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
- */
- NSString *_mappedToKey; ///< 映射 key
- NSArray *_mappedToKeyPath; ///< 映射 keyPath,如果没有映射到 keyPath 则返回 nil
- NSArray *_mappedToKeyArray; ///< key 或者 keyPath 的数组,如果没有映射多个键的话则返回 nil
- YYClassPropertyInfo *_info; ///< 属性信息,详见上文 YYClassPropertyInfo && property_t 章节
- _YYModelPropertyMeta *_next; ///< 如果有多个属性映射到同一个 key 则指向下一个模型属性元
- }
- @end
_YYModelMeta
- @interface _YYModelMeta : NSObject {
- @package
- YYClassInfo *_classInfo;
- /// Key:被映射的 key 与 keyPath, Value:_YYModelPropertyMeta.
- NSDictionary *_mapper;
- /// Array<_YYModelPropertyMeta>, 当前模型的所有 _YYModelPropertyMeta 数组
- NSArray *_allPropertyMetas;
- /// Array<_YYModelPropertyMeta>, 被映射到 keyPath 的 _YYModelPropertyMeta 数组
- NSArray *_keyPathPropertyMetas;
- /// Array<_YYModelPropertyMeta>, 被映射到多个 key 的 _YYModelPropertyMeta 数组
- NSArray *_multiKeysPropertyMetas;
- /// 映射 key 与 keyPath 的数量,等同于 _mapper.count
- NSUInteger _keyMappedCount;
- /// 模型 class 类型
- YYEncodingNSType _nsType;
- // 忽略
- ...
- }
- @end
三、使用
1.简单Model与JSON相互转换
- #import <Foundation/Foundation.h>
- //"time":"2018-07-04 12:13:52",
- //"ftime":"2018-07-04 12:13:52",
- //"context":"快件已签收 签收人: 他人代收 感谢使用圆通速递,期待再次为您服
- @interface IOALogisticsDetailModel : NSObject
- @property(nonatomic,copy)NSString *time;
- @property(nonatomic,copy)NSString *ftime;
- @property(nonatomic,copy)NSString *context;
- @end
- #import "IOALogisticsDetailModel.h"
- @implementation IOALogisticsDetailModel
- @end
下面是运用
- - (NSArray <IOALogisticsDetailModel *>*)setupOrderWithArray:(NSArray <NSDictionary *>*)array{
- NSMutableArray <IOALogisticsDetailModel *>*modelArray = [NSMutableArray arrayWithCapacity:array.count];
- for(NSDictionary *dic in array){
- IOALogisticsDetailModel *model = [IOALogisticsDetailModel yy_modelWithDictionary:dic];
- if (!model) continue;
- [modelArray addObject:model];
- }
- return modelArray;
- }
红色部分就是应用。
如果需要Model转为JSON如下
- #import <Foundation/Foundation.h>
- @interface IOAOrderAftersaleRequestModel : NSObject
- @property(nonatomic,copy)NSString *order_sn;
- @property(nonatomic,copy)NSString *rec_id;
- @property(nonatomic,copy)NSString *is_type;
- @property(nonatomic,assign)NSInteger refund_count;
- @property(nonatomic,copy)NSString *content;
- @property(nonatomic,copy)NSString *return_attachs;
- @property(nonatomic,copy)NSString *shop_id;
- @property(nonatomic,copy)NSString *reason;
- @property(nonatomic,assign)float total;
- @end
- #import "IOAOrderAftersaleRequestModel.h"
- @implementation IOAOrderAftersaleRequestModel
- @end
下面是运用
- //提交退货商品
- @interface IOAOrderAftersaleRequest:IOARequest
- @property (nonatomic,strong)IOAOrderAftersaleRequestModel *requestModel;
- @end
- //提交退货商品
- @implementation IOAOrderAftersaleRequest
- - (id)requestArgument{
- NSMutableDictionary *dic = [IOAApiManager getParametersWithService:@"App.Order.SetOrderAftersaleGoodsrefundsList"];
- NSDictionary *temDic = [self.requestModel yy_modelToJSONObject];
- [dic addEntriesFromDictionary:temDic];
- return dic;
- }
2.Model属性名与JSON中key不同
- // JSON:
- {
- "n":"Harry Pottery",
- "p": ,
- "ext" : {
- "desc" : "A book written by J.K.Rowing."
- },
- "ID" :
- }
- // Model:
- @interface Book : NSObject
- @property NSString *name;
- @property NSInteger page;
- @property NSString *desc;
- @property NSString *bookID;
- @end
- @implementation Book
- //返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。
- + (NSDictionary *)modelCustomPropertyMapper {
- return @{@"name" : @"n",
- @"page" : @"p",
- @"desc" : @"ext.desc",
- @"bookID" : @[@"id",@"ID",@"book_id"]};
- }
3.Model包含Model
- // JSON
- {
- "author":{
- "name":"J.K.Rowling",
- "birthday":"1965-07-31T00:00:00+0000"
- },
- "name":"Harry Potter",
- "pages":
- }
- // Model: 什么都不用做,转换会自动完成
- @interface Author : NSObject
- @property NSString *name;
- @property NSDate *birthday;
- @end
- @implementation Author
- @end
- @interface Book : NSObject
- @property NSString *name;
- @property NSUInteger pages;
- @property Author *author; //Book 包含 Author 属性
- @end
- @implementation Book
- @end
下面在我们项目中的使用
- #import <Foundation/Foundation.h>
- #import "IOAOrder.h"
- @interface IOAOrderGroup : NSObject
- @property(nonatomic,copy)NSString *order_id;
- @property(nonatomic,copy)NSString *parent_sn;
- @property(nonatomic,copy)NSString *order_sn;
- @property(nonatomic,copy)NSString *order_status;
- @property(nonatomic,copy)NSString *refund_status;
- @property(nonatomic,copy)NSString *return_status;
- @property(nonatomic,copy)NSString *pay_status;
- @property(nonatomic,copy)NSString *total_amount;
- @property(nonatomic,copy)NSString *company_name;
- @property(nonatomic,copy)NSString *company_logo;
- @property(nonatomic,copy)NSString *shop_id;
- @property(nonatomic,copy)NSString *stroe_id;
- @property(nonatomic,copy)NSString *order_amount;
- @property(nonatomic,assign)int store_id;
- @property (nonatomic,strong)NSArray<IOAOrder *> *goods_list;
- @end
- #import "IOAOrderGroup.h"
- #import <YYModel/YYModel.h>
- @implementation IOAOrderGroup
- + (NSDictionary *)modelContainerPropertyGenericClass{
- return @{@"goods_list":[IOAOrder class]};
- }
- @end
- #import <Foundation/Foundation.h>
- @interface IOAOrder : NSObject
- @property(nonatomic,copy)NSString *rec_id;
- @property(nonatomic,copy)NSString *order_id;
- @property(nonatomic,copy)NSString *brand_name;
- @property(nonatomic,copy)NSString *goods_id;
- @property(nonatomic,copy)NSString *goods_name;
- @property(nonatomic,copy)NSString *goods_sn;
- @property(nonatomic,copy)NSString *goods_num;
- @property(nonatomic,copy)NSString *market_price;
- @property(nonatomic,copy)NSString *goods_price;
- @property(nonatomic,copy)NSString *cost_price;
- @property(nonatomic,copy)NSString *member_goods_price;
- @property(nonatomic,copy)NSString *total_price;
- @property(nonatomic,copy)NSString *give_integral;
- @property(nonatomic,copy)NSString *spec_key;
- @property(nonatomic,copy)NSString *unit;
- @property(nonatomic,copy)NSString *spec_key_name;
- @property(nonatomic,copy)NSString *bar_code;
- @property(nonatomic,copy)NSString *is_comment;
- @property(nonatomic,copy)NSString *prom_type;
- @property(nonatomic,copy)NSString *prom_id;
- @property(nonatomic,copy)NSString *is_send;
- @property(nonatomic,copy)NSString *delivery_id;
- @property(nonatomic,copy)NSString *add_time;
- @property(nonatomic,copy)NSString *update_time;
- @property(nonatomic,copy)NSString *image_url;
- @property(nonatomic,assign)BOOL selected;
- @end
- #import "IOAOrder.h"
- @implementation IOAOrder
- @end
4.白名单黑名单
- @interface User
- @property NSString *name;
- @property NSUInteger age;
- @end
- @implementation Attributes
- // 如果实现了该方法,则处理过程中会忽略该列表内的所有属性
- + (NSArray *)modelPropertyBlacklist {
- return @[@"test1", @"test2"];
- }
- // 如果实现了该方法,则处理过程中不会处理该列表外的属性。
- + (NSArray *)modelPropertyWhitelist {
- return @[@"name"];
- }
- @end
YYModel的核心是通过runtime获取结构体中得Ivars的值,将此值定义为key,然后给key赋value值,所以我们需要自己遍历容器(NSArray,NSSet,NSDictionary),获取每一个值,然后KVC。
YYModel底层解析- Runtime的更多相关文章
- 【iOS 单例设计模式】底层解析与运用
[iOS 单例设计模式]底层解析与运用 一.单例设计名词解释: (官方解释)单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例.(形象比喻)程序 — 公司 单例实例 - 管理 ...
- 浅谈 Java Xml 底层解析方式
XML 使用DTD(document type definition)文档类型来标记数据和定义数据,格式统一且跨平台和语言,已成为业界公认的标准. 目前 XML 描述数据龙头老大的地位渐渐受到 Jso ...
- iOS - Block底层解析
Block是iOS开发中一种比较特殊的数据结构,它可以保存一段代码,在合适的地方再调用,具有语法简介.回调方便.编程思路清晰.执行效率高等优点,受到众多猿猿的喜爱.但是Block在使用过程中,如果对B ...
- javaSE高级篇6 — 注解( 附:注解底层解析 ) —— 更新完毕
注解 ---- 英文:annotation 1.注解长什么样子? @xxxxxxx( 一些信息 ) ----- 这个信息可有可无 2.注解可以放在什么地方? 类本身的上面.属性的上面.方法的上面.参数 ...
- SpringMVC----执行流程+底层解析
SpringMVC流程图如上面所示,根据上图,串联一下底层源码: 1.在DispatcherServlet中找到doDisPatch 2.观察方法体,然后找到getHandler方法 3.点进方法,发 ...
- iOS 底层解析weak的实现原理(包含weak对象的初始化,引用,释放的分析)
原文 很少有人知道weak表其实是一个hash(哈希)表,Key是所指对象的地址,Value是weak指针的地址数组.更多人的人只是知道weak是弱引用,所引用对象的计数器不会加一,并在引用对象被释放 ...
- Asp.Net底层解析
写的很好的一篇文章,但由于不能转载 所以把链接发在这里,以方便自己以后看 http://blog.csdn.net/mlcactus/article/details/8564347 http://ji ...
- JavaScript闭包底层解析
1. 闭包是一个函数,这个函数有权访问另一个函数作用域中的变量,创建闭包最常见的方式,就是在函数内部创建函数.要想彻底搞清其中细节,必须从函数从创建到调用的时候都发生了什么入手 2. 函数第一次被调用 ...
- Hbase底层解析
hfile+compaction 原理 用户数据写入先写WAL,再写缓存,满足一定条件后缓存数据会执行flush操作真正落盘,形成一个数据文件HFile.太多数据文件会导致数据查询IO次数增多,因 ...
随机推荐
- nginx 静态目录配置规则
1.子目录匹配 如下配置 location / { root /data/www; } 访问http://127.0.0.1/时,配匹配/data/www 访问http://127.0.0.1/ima ...
- 用kattle将数据从SQLserver中导入到vertica中
今天简单的学习了一下ETL工具kattle了,只是简单的上手,不过这也已经够我去做POC了. 首先大体介绍一下kattle,Kettle是一款国外开源的ETL工具,纯java编写,可以在Window. ...
- Json对象与Json字符串的转化、JSON字符串与Java对象的转换
一.Json对象与Json字符串的转化 1.jQuery插件支持的转换方式: $.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符 ...
- Nginx负载均衡的5种策略(转载)
Nginx的upstream目前支持的5种方式的分配 轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除. upstream backserver { s ...
- echarts (geo/map) 渐变效果
这两天帮人搞了下中国范围内仓库量统计的需求,查了下echarts 里的文档找到类似的demo(链接:https://ecomfe.github.io/echarts-examples/public/e ...
- cadence电路板布线
设置完约束规则后,便可以开始电路板的布线工作.
- lua语言自学知识点----Lua与.Net相互调用
知识点: LuaInterface作用是用来完成Lua与C#的相互调用. LuaInterface核心库:1.luainterface.dll 用于C#读取lua(放在bin目录同级) 2.luane ...
- libguestfs手册(1): 架构
要编辑一个image,则运行下面的命令 guestfish -a ubuntutest.img ><fs> 会弹出一个命令行工具 运行run ><fs> run 我 ...
- Eclipse格式化整个项目
Eclipse有一个非常好的功能,就是把源代码进行美化(或者是标准化),在打开的Java源代码中,Ctrl+Shift+F就可做到. 但是,如果你想把整个项目中的源代码都美化一下呢?这里有一个简单的办 ...
- [Swift]LeetCode423. 从英文中重建数字 | Reconstruct Original Digits from English
Given a non-empty string containing an out-of-order English representation of digits 0-9, output the ...