Преглед на файлове

add description method for YYModel

ibireme преди 9 години
родител
ревизия
3cab90d42e
променени са 5 файла, в които са добавени 303 реда и са изтрити 7 реда
  1. 4 0
      Framework/YYModel.xcodeproj/project.pbxproj
  2. 6 4
      README.md
  3. 7 0
      YYModel/NSObject+YYModel.h
  4. 155 3
      YYModel/NSObject+YYModel.m
  5. 131 0
      YYModelTests/YYTestDescription.m

+ 4 - 0
Framework/YYModel.xcodeproj/project.pbxproj

@@ -8,6 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		AB1DAC8F1C0AF02B00442613 /* YYTestModelToJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = AB1DAC8E1C0AF02B00442613 /* YYTestModelToJSON.m */; };
+		AB5032881C4627B100FC6C42 /* YYTestDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = AB5032871C4627B100FC6C42 /* YYTestDescription.m */; };
 		ABA06CAC1C08514D00AD2108 /* YYModel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9D41A071BD0FABE00CD8EBF /* YYModel.framework */; };
 		ABA06CB21C08566100AD2108 /* NSObject+YYModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D9D41A171BD0FB3300CD8EBF /* NSObject+YYModel.m */; };
 		ABA06CB31C08566100AD2108 /* YYClassInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = D9D41A191BD0FB3300CD8EBF /* YYClassInfo.m */; };
@@ -39,6 +40,7 @@
 
 /* Begin PBXFileReference section */
 		AB1DAC8E1C0AF02B00442613 /* YYTestModelToJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYTestModelToJSON.m; sourceTree = "<group>"; };
+		AB5032871C4627B100FC6C42 /* YYTestDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYTestDescription.m; sourceTree = "<group>"; };
 		ABA06CA71C08514D00AD2108 /* YYModelTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = YYModelTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
 		ABA06CB51C08589300AD2108 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		ABA06CB91C08595900AD2108 /* YYTestModelMapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYTestModelMapper.m; sourceTree = "<group>"; };
@@ -93,6 +95,7 @@
 				D95943EF1C0B6467002D88BD /* YYTestBlacklistWhitelist.m */,
 				ABFEC7181C0BE7A900B3D8C5 /* YYTestCustomTransform.m */,
 				ABFEC71A1C0BF23200B3D8C5 /* YYTestCustomClass.m */,
+				AB5032871C4627B100FC6C42 /* YYTestDescription.m */,
 				ABA06CB51C08589300AD2108 /* Info.plist */,
 			);
 			name = YYModelTests;
@@ -253,6 +256,7 @@
 				D95943F01C0B6467002D88BD /* YYTestBlacklistWhitelist.m in Sources */,
 				ABA06CB21C08566100AD2108 /* NSObject+YYModel.m in Sources */,
 				ABA06CBA1C08595900AD2108 /* YYTestModelMapper.m in Sources */,
+				AB5032881C4627B100FC6C42 /* YYTestDescription.m in Sources */,
 				D9B26FF61C08D13F004880F0 /* YYTestHelper.m in Sources */,
 				ABFEC71D1C0C284B00B3D8C5 /* YYTestNestModel.m in Sources */,
 				D9B270271C09FDF8004880F0 /* YYTestAutoTypeConvert.m in Sources */,

+ 6 - 4
README.md

@@ -30,7 +30,7 @@ Features
 - **Type Safe**: All data types will be verified to ensure type-safe during the conversion process.
 - **Non-intrusive**: There is no need to make the model class inherit from other base class.
 - **Lightwight**: This library contains only 5 files.
-- **Docs and unit testing**: Coverage 100%.
+- **Docs and unit testing**: 100% docs coverage, 99% code coverage.
 
 Usage
 ==============
@@ -246,7 +246,7 @@ You can map a json key (key path) or an array of json key (key path) to one or m
 	}
 	@end
 
-###Coding/Copying/hash/equal
+###Coding/Copying/hash/equal/description
 
 	@interface YYShadow :NSObject <NSCoding, NSCopying>
 	@property (nonatomic, copy) NSString *name;
@@ -259,6 +259,7 @@ You can map a json key (key path) or an array of json key (key path) to one or m
 	- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
 	- (NSUInteger)hash { return [self yy_modelHash]; }
 	- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
+	- (NSString *)description { return [self yy_modelDescription]; }
 	@end
 
 
@@ -327,7 +328,7 @@ YYModel is provided under the MIT license. See LICENSE file for details.
 - **类型安全**: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。
 - **无侵入性**: 模型无需继承自其他基类。
 - **轻量**: 该框架只有 5 个文件 (包括.h文件)。
-- **文档和单元测试**: 覆盖率 100%。
+- **文档和单元测试**: 文档覆盖率100%, 代码覆盖率99%。
 
 使用方法
 ==============
@@ -555,7 +556,7 @@ EEE MMM dd HH:mm:ss Z yyyy
 	}
 	@end
 
-###Coding/Copying/hash/equal
+###Coding/Copying/hash/equal/description
 
 	@interface YYShadow :NSObject <NSCoding, NSCopying>
 	@property (nonatomic, copy) NSString *name;
@@ -569,6 +570,7 @@ EEE MMM dd HH:mm:ss Z yyyy
 	- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
 	- (NSUInteger)hash { return [self yy_modelHash]; }
 	- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
+	- (NSString *)description { return [self yy_modelDescription]; }
 	@end
 
 

+ 7 - 0
YYModel/NSObject+YYModel.h

@@ -201,6 +201,13 @@
  */
 - (BOOL)yy_modelIsEqual:(id)model;
 
+/**
+ Description method for debugging purposes based on properties.
+ 
+ @return A string that describes the contents of the receiver.
+ */
+- (NSString *)yy_modelDescription;
+
 @end
 
 

+ 155 - 3
YYModel/NSObject+YYModel.m

@@ -409,11 +409,11 @@ static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic
     @package
     /// Key:mapped key and key path, Value:_YYModelPropertyInfo.
     NSDictionary *_mapper;
-    /// Array<_YYModelPropertyInfo>, all property meta of this model.
+    /// Array<_YYModelPropertyMeta>, all property meta of this model.
     NSArray *_allPropertyMetas;
-    /// Array<_YYModelPropertyInfo>, property meta which is mapped to a key path.
+    /// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
     NSArray *_keyPathPropertyMetas;
-    /// Array<_YYModelPropertyInfo>, property meta which is mapped to multi keys.
+    /// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
     NSArray *_multiKeysPropertyMetas;
     /// The number of mapped key (and key path), same to _mapper.count.
     NSUInteger _keyMappedCount;
@@ -1224,6 +1224,154 @@ static id ModelToJSONObjectRecursive(NSObject *model) {
     return result;
 }
 
+/// Add indent to string (exclude first line)
+static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {
+    for (NSUInteger i = 0, max = desc.length; i < max; i++) {
+        unichar c = [desc characterAtIndex:i];
+        if (c == '\n') {
+            for (NSUInteger j = 0; j < indent; j++) {
+                [desc insertString:@"    " atIndex:i + 1];
+            }
+            i += indent * 4;
+            max += indent * 4;
+        }
+    }
+    return desc;
+}
+
+/// Generaate a description string
+static NSString *ModelDescription(NSObject *model) {
+    static const int kDescMaxLength = 100;
+    if (!model) return @"<nil>";
+    if (model == (id)kCFNull) return @"<null>";
+    if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
+    
+    
+    _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class];
+    switch (modelMeta->_nsType) {
+        case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
+            return [NSString stringWithFormat:@"\"%@\"",model];
+        }
+        
+        case YYEncodingTypeNSValue:
+        case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
+            NSString *tmp = model.description;
+            if (tmp.length > kDescMaxLength) {
+                tmp = [tmp substringToIndex:kDescMaxLength];
+                tmp = [tmp stringByAppendingString:@"..."];
+            }
+            return tmp;
+        }
+            
+        case YYEncodingTypeNSNumber:
+        case YYEncodingTypeNSDecimalNumber:
+        case YYEncodingTypeNSDate:
+        case YYEncodingTypeNSURL: {
+            return [NSString stringWithFormat:@"%@",model];
+        }
+            
+        case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: {
+            model = ((NSSet *)model).allObjects;
+        } // no break
+            
+        case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {
+            NSArray *array = (id)model;
+            NSMutableString *desc = [NSMutableString new];
+            if (array.count == 0) {
+                 return [desc stringByAppendingString:@"[]"];
+            } else {
+                [desc appendFormat:@"[\n"];
+                for (NSUInteger i = 0, max = array.count; i < max; i++) {
+                    NSObject *obj = array[i];
+                    [desc appendString:@"    "];
+                    [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
+                    [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
+                }
+                [desc appendString:@"]"];
+                return desc;
+            }
+        }
+        case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: {
+            NSDictionary *dic = (id)model;
+            NSMutableString *desc = [NSMutableString new];
+            if (dic.count == 0) {
+                return [desc stringByAppendingString:@"{}"];
+            } else {
+                NSArray *keys = dic.allKeys;
+                
+                [desc appendFormat:@"[\n"];
+                for (NSUInteger i = 0, max = keys.count; i < max; i++) {
+                    NSString *key = keys[i];
+                    NSObject *value = dic[key];
+                    [desc appendString:@"    "];
+                    [desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
+                    [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
+                }
+                [desc appendString:@"]"];
+            }
+            return desc;
+        }
+        
+        default: {
+            NSMutableString *desc = [NSMutableString new];
+            [desc appendFormat:@"<%@: %p>", model.class, model];
+            if (modelMeta->_allPropertyMetas.count == 0) return desc;
+            
+            // sort property names
+            NSArray *properties = [modelMeta->_allPropertyMetas
+                                   sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) {
+                                       return [p1->_name compare:p2->_name];
+                                   }];
+            
+            [desc appendFormat:@" {\n"];
+            for (NSUInteger i = 0, max = properties.count; i < max; i++) {
+                _YYModelPropertyMeta *property = properties[i];
+                NSString *propertyDesc;
+                if (property->_isCNumber) {
+                    NSNumber *num = ModelCreateNumberFromProperty(model, property);
+                    propertyDesc = num.stringValue;
+                } else {
+                    switch (property->_type & YYEncodingTypeMask) {
+                        case YYEncodingTypeObject: {
+                            id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
+                            propertyDesc = ModelDescription(v);
+                            if (!propertyDesc) propertyDesc = @"<nil>";
+                        } break;
+                        case YYEncodingTypeClass: {
+                            id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
+                            propertyDesc = ((NSObject *)v).description;
+                            if (!propertyDesc) propertyDesc = @"<nil>";
+                        } break;
+                        case YYEncodingTypeSEL: {
+                            SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
+                            if (sel) propertyDesc = NSStringFromSelector(sel);
+                            else propertyDesc = @"<NULL>";
+                        } break;
+                        case YYEncodingTypeBlock: {
+                            id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
+                            propertyDesc = block ? ((NSObject *)block).description : @"<nil>";
+                        } break;
+                        case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: {
+                            void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
+                            propertyDesc = [NSString stringWithFormat:@"%p",pointer];
+                        } break;
+                        case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
+                            NSValue *value = [model valueForKey:property->_name];
+                            propertyDesc = value ? value.description : @"{unknown}";
+                        } break;
+                        default: propertyDesc = @"<unknown>";
+                    }
+                }
+                
+                propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
+                [desc appendFormat:@"    %@ = %@",property->_name, propertyDesc];
+                [desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
+            }
+            [desc appendFormat:@"}"];
+            return desc;
+        }
+    }
+}
 
 
 @implementation NSObject (YYModel)
@@ -1552,6 +1700,10 @@ static id ModelToJSONObjectRecursive(NSObject *model) {
     return YES;
 }
 
+- (NSString *)yy_modelDescription {
+    return ModelDescription(self);
+}
+
 @end
 
 

+ 131 - 0
YYModelTests/YYTestDescription.m

@@ -0,0 +1,131 @@
+//
+//  YYTestDescription.m
+//  YYModel <https://github.com/ibireme/YYModel>
+//
+//  Created by ibireme on 16/1/3.
+//  Copyright (c) 2016 ibireme.
+//
+//  This source code is licensed under the MIT-style license found in the
+//  LICENSE file in the root directory of this source tree.
+//
+
+#import <XCTest/XCTest.h>
+#import "YYModel.h"
+
+
+typedef struct my_struct {
+    int a;
+    double b;
+    long double c;
+} my_struct;
+
+typedef struct my_union {
+    int a;
+    double b;
+    long double c;
+} my_union;
+
+
+
+@interface YYTestDescriptionModel : NSObject
+@property bool boolValue;
+@property BOOL BOOLValue;
+@property char charValue;
+@property unsigned char unsignedCharValue;
+@property short shortValue;
+@property unsigned short unsignedShortValue;
+@property int intValue;
+@property unsigned int unsignedIntValue;
+@property long longValue;
+@property unsigned long unsignedLongValue;
+@property long long longLongValue;
+@property unsigned long long unsignedLongLongValue;
+@property float floatValue;
+@property double doubleValue;
+@property long double longDoubleValue;
+@property (strong) Class classValue;
+@property SEL selectorValue;
+@property (copy) void (^blockValue)();
+@property void *pointerValue;
+@property char *cString;
+@property CFArrayRef cfArrayValue;
+@property NSValue *valueValue;
+
+@property CGSize sizeValue;
+@property CGPoint pointValue;
+@property CGRect rectValue;
+@property CGAffineTransform transformValue;
+@property UIEdgeInsets insetsValue;
+@property UIOffset offsetValue;
+@property my_struct myStructValue;        // invalid for NSKeyedArchiver/Unarchiver
+@property my_union myUnionValue;        // invalid for NSKeyedArchiver/Unarchiver
+
+
+@property (nonatomic, strong) NSObject *object;
+@property (nonatomic, strong) NSNumber *number;
+@property (nonatomic, strong) NSDecimalNumber *decimal;
+@property (nonatomic, strong) NSString *string;
+@property (nonatomic, strong) NSString *string2;
+@property (nonatomic, strong) NSMutableString *mString;
+@property (nonatomic, strong) NSData *data;
+@property (nonatomic, strong) NSMutableData *mData;
+@property (nonatomic, strong) NSDate *date;
+@property (nonatomic, strong) NSValue *value;
+@property (nonatomic, strong) NSURL *url;
+
+@property (nonatomic, strong) NSArray *array;
+@property (nonatomic, strong) NSMutableArray *mArray;
+@property (nonatomic, strong) NSDictionary *dict;
+@property (nonatomic, strong) NSMutableDictionary *mDict;
+@property (nonatomic, strong) NSSet *set;
+@property (nonatomic, strong) NSMutableSet *mSet;
+@end
+
+@implementation YYTestDescriptionModel
+- (NSString *)description {
+    return [self yy_modelDescription];
+}
+@end
+
+
+
+
+@interface YYTestDescription : XCTestCase
+
+@end
+
+@implementation YYTestDescription
+
+- (void)testDescription {
+    YYTestDescriptionModel *model = [YYTestDescriptionModel new];
+    model.string = @"test";
+    model.intValue = 100;
+    
+    model.number = @(123);
+    model.decimal = [NSDecimalNumber decimalNumberWithString:@"456"];
+    model.value = [NSValue valueWithRange:NSMakeRange(10, 5)];
+    model.date = [NSDate new];
+    model.blockValue = ^{};
+    model.mData = [NSMutableData data];
+    for (int i = 0; i < 1024; i++) {
+        [model.mData appendBytes:&i length:sizeof(int)];
+    }
+    model.array = @[];
+    model.dict = @{};
+    model.set = [NSSet new];
+    model.mArray = [NSMutableArray new];
+    model.mDict = [NSMutableDictionary new];
+    model.mSet = [NSMutableSet new];
+    
+    for (int i = 0; i < 2; i++) {
+        YYTestDescriptionModel *sub = [YYTestDescriptionModel new];
+        sub.intValue = i;
+        [model.mArray addObject:sub];
+        model.mDict[@(i).description] = sub;
+        [model.mSet addObject:sub];
+    }
+    
+    NSLog(@"%@",model.description);
+}
+
+@end