做一件事儿肯定是有原因,我来做这个事儿的原因,是因为我鹏哥思考需要在,路漫漫其修远兮,上下求索。
然后...鑫哥觉得,要成为大牛,首先要做的就是先跟大牛学习下,那么就抄一下大牛的代码吧...(我觉得很对)
一步一步走走看看,那么就先从我们最为常用的MJEX来入手,我也来copy一遍代码来试试
根据以前的样子,那么我先把目录写下来,然后进行一步一步一步的摩擦,且来看看👀👀
MJExtension
引入头文件真的没什么可说的
MJExtension.h作为一个.h的头文件引入类,可谓是清新脱俗,简单明了 ...巴拉巴拉巴拉
然后下一步开始逐渐解析MJEX
MJExtensionConst
方法过期使用
#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)
这个方法在设计库或者sdk的时候会使用到,然后标注显示
具体使用的是如下方法
@property(nonatomic, copy)NSString *gdtype __attribute__((deprecated("已过期, 用goodsclassifytype替换")));
构建错误
#define MJExtensionBuildError(clazz, msg) \
NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \
[clazz setMj_error:error];
其中,自定义错误域对象CustomErrorDomain,通常用域名反写,也可以是任何其他字符串code错误标识, 系统的code一般都大于零,自定code可以用枚举(最好用负数, 但不是必须的)userInfo自定义错误信息,NSLocalizedDescriptionKey是NSError头文件中预定义的键,标识错误的本地化描述
可以通过NSError的localizedDescription方法获得对应的值信息
详细介绍NSError的可以查看iphone跬步之--错误信息 NSError
日志输出
这一块代码没什么可以讲解的,就是log的打印
#ifdef DEBUG
#define MJExtensionLog(...) NSLog(__VA_ARGS__)
#else
#define MJExtensionLog(...)
#endif
使用
#define NSLog(fmt, ...) NSLog((@"%s [Line: %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
这个打印,会打印出你的类名方法名和对应的行数[当做平时的一个小知识来记忆吧~]
剩下的这一堆是断言的使用
/**
* 断言
* @param condition 条件
* @param returnValue 返回值
*/
#define MJExtensionAssertError(condition, returnValue, clazz, msg) \
[clazz setMj_error:nil]; \
if ((condition) == NO) { \
MJExtensionBuildError(clazz, msg); \
return returnValue;\
}
#define MJExtensionAssert2(condition, returnValue) \
if ((condition) == NO) return returnValue;
/**
* 断言
* @param condition 条件
*/
#define MJExtensionAssert(condition) MJExtensionAssert2(condition, )
/**
* 断言
* @param param 参数
* @param returnValue 返回值
*/
#define MJExtensionAssertParamNotNil2(param, returnValue) \
MJExtensionAssert2((param) != nil, returnValue)
/**
* 断言
* @param param 参数
*/
#define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, )```
>断言:NSAssert()是一个宏,用于开发阶段调试程序中的Bug,通过为NSAssert()传递条件表达式来断定是否属于Bug,满足条件返回真值,程序继续运行,如果返回假值,则抛出异常,并且可以自定义异常描述。
这里引用下别人对于[断言的介绍](http://www.jianshu.com/p/6e444981ab45)
MJEX将打印所有的属性使用宏定义在Const里面写出,为了可以在全局中方便的调用
/**
- 打印所有的属性
*/
define MJLogAllIvars \
-(NSString *)description
{
return [self mj_keyValues].description;
}
define MJExtensionLogAllProperties MJLogAllIvars
直接调用`MJLogAllIvars `的宏定义就可以直接输出所有属性
类属性的使用
/**
- 类型(属性类型)
*/
extern NSString *const MJPropertyTypeInt;
extern NSString *const MJPropertyTypeShort;
extern NSString *const MJPropertyTypeFloat;
extern NSString *const MJPropertyTypeDouble;
extern NSString *const MJPropertyTypeLong;
extern NSString *const MJPropertyTypeLongLong;
extern NSString *const MJPropertyTypeChar;
extern NSString *const MJPropertyTypeBOOL1;
extern NSString *const MJPropertyTypeBOOL2;
extern NSString *const MJPropertyTypePointer;
extern NSString *const MJPropertyTypeIvar;
extern NSString *const MJPropertyTypeMethod;
extern NSString *const MJPropertyTypeBlock;
extern NSString *const MJPropertyTypeClass;
extern NSString *const MJPropertyTypeSEL;
extern NSString *const MJPropertyTypeId;
从这个类属性里面,我们可以清楚的了解到MJEX对于某些类的拓展使用
/**
- 成员变量类型(属性类型)
*/
NSString *const MJPropertyTypeInt = @"i";
NSString *const MJPropertyTypeShort = @"s";
NSString *const MJPropertyTypeFloat = @"f";
NSString *const MJPropertyTypeDouble = @"d";
NSString *const MJPropertyTypeLong = @"l";
NSString *const MJPropertyTypeLongLong = @"q";
NSString *const MJPropertyTypeChar = @"c";
NSString *const MJPropertyTypeBOOL1 = @"c";
NSString *const MJPropertyTypeBOOL2 = @"b";
NSString const MJPropertyTypePointer = @"";
NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}";
NSString *const MJPropertyTypeMethod = @"^{objc_method=}";
NSString *const MJPropertyTypeBlock = @"@?";
NSString *const MJPropertyTypeClass = @"#";
NSString *const MJPropertyTypeSEL = @":";
NSString *const MJPropertyTypeId = @"@";
【借鉴】对于.m里面的赋值,从而借鉴可以从编辑全局的某些text或者Toast的信息可以使用上方式作为统一的管理:
例如
.h
`extern NSString *const MJToast_Success;`
.m
`NSString *const MJToast_Success = @"保存成功"`
从而在全局统一使用
##MJFoundation
继承自`@interface MJFoundation : NSObject`只用一个方法
`+ (BOOL)isClassFromFoundation:(Class)c;`
>MJ是这样注释的----
集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断
![使用NSSet将数据的不属于NSObj的类加入.png](http://upload-images.jianshu.io/upload_images/693139-25f36e7cfb54e302.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
>【注意】NSSet和NSMutableSet是无序的!!!!看准咯是无序的!因为它本身的排序是使用的哈希排序(划重点)
而且他是唯一的!!里面的每一个数据都是唯一的!!!!(划重点)
之前我遇到的问题也是如此,讨厌于双重for循环去去重,直接使用了NSMutableSet,但是没有注意到是无序的序列,对于需要排序的,建议写一个字段排序的方法,这里不再做赘述
##MJProperty -- 包装一个成员属性
关于如何包装一个成员属性呢,那么我们先看一下MJ如何去做的
`/** 成员属性 */
@property (nonatomic, assign) objc_property_t property;`
> objc_property_t
···what is this?
[iOS反射机制: objc_property_t的使用](https://segmentfault.com/a/1190000004520289)
这为大哥写的蛮详细,这里做简略的介绍
`/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;`
apple将其称为一个隐藏的类型,OC中的一个声明属性。
>iOS属性反射:说白了,就是将两个对象的所有属性,用动态的方式取出来,并根据属性名,自动绑值。(注意:对象的类,如果是派生类,就得靠其他方式来实现了,因为得到不该基类的属性。)
常用的反射方式,有如下两种:
>2-从一个NSDictionary->自定义实体类(此方式最最常用,如网络Json数据会组成NSDictionary。sqlite查询数据,可以用第三方组件组成NSDictionary)
直接上码,(这里码在NSObject类别中)
获取对象所有属性:
我注意到MJEX使用了这样一个属性---[关联](http://blog.csdn.net/onlyou930/article/details/9299169)
![关联属性-MJEX.png](http://upload-images.jianshu.io/upload_images/693139-5f145a81a8f9ee75.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这个类的核心代码如下,我么一段一段进行解析
// 通过字符串key创建对应的keys
```objective-c
- (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey
{
if (stringKey.length == 0) return nil;
NSMutableArray *propertyKeys = [NSMutableArray array];
// 如果有多级映射
NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."];
for (NSString *oldKey in oldKeys) {
NSUInteger start = [oldKey rangeOfString:@"["].location;
if (start != NSNotFound) { // 有索引的key
NSString *prefixKey = [oldKey substringToIndex:start];
NSString *indexKey = prefixKey;
if (prefixKey.length) {
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
propertyKey.name = prefixKey;
[propertyKeys addObject:propertyKey];
indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""];
}
/** 解析索引 **/
// 元素
NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"];
for (NSInteger i = 0; i<cmps.count - 1; i++) {
MJPropertyKey *subPropertyKey = [[MJPropertyKey alloc] init];
subPropertyKey.type = MJPropertyKeyTypeArray;
subPropertyKey.name = cmps[i];
[propertyKeys addObject:subPropertyKey];
}
} else { // 没有索引的key
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
propertyKey.name = oldKey;
[propertyKeys addObject:propertyKey];
}
}
return propertyKeys;
}
采用多级映射的方式,将对应的关联key值进行