RunTime 이해와 실전(二)
JSON에서 모델로의 매핑 원리
평소에 우리는 이와 같은 라이브러리를 어떻게 사용하는지 생각해 보자. JSON이 있을 때 우리는 모든 JSON의 필드(예를 들어name, 페이지)를 대응하는 클래스의 속성으로 쓴다.그리고 라이브러리는 자동으로 JSON에 대응하는 필드의 값을 대응하는 속성에 부여합니다.속성은 @property로 정의합니다. 컴파일러가 대응하는 get
set , . get
set 방법을 만들어서 값을 부여할 수 있음을 의미합니다.Objective-C에 유명한 함수 objcmsgSend(...) 우리의 모든 유사한 [obj method] 방법은 (메시지 보내기)objc 로 변환됩니다msgSend(...) 라는 식으로 호출했다.그래서 한 라이브러리에서 이 모델의set 방법을 사용하려면objcmsgSend(...) 훨씬 쉬울 거예요. 그래서 JSON이 모델에 비치는 원리는 사실 이 함수를 호출하는 거예요.그래서 전체 프로세스는 당신이 나에게 모델 클래스를 주면 나는runtime가 제공하는 각종 함수로 당신의 모든 속성과 대응하는 get`set을 얻을 것입니다. 상응하는 유형을 판단한 후에objc 를 호출합니다.msgSend(...).
json과 사전의 변환
//
+ (NSString*)DataTOjsonString:(id)object
{
NSString *jsonString = nil;
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
options:NSJSONWritingPrettyPrinted
// Pass 0 if you don't care about the readability of the generated string
error:&error];
if (! jsonData) {
NSLog(@"Got an error: %@", error);
} else {
jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
return jsonString;
}
//
+ (NSDictionary *)ww_dictionaryWithJSON:(id)json{
if (!json || json == (id)kCFNull) {
return nil;
}
NSDictionary *dic = nil;
NSData *data = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
}else if ([json isKindOfClass:[NSString class]]) {
data = [(NSString *)json dataUsingEncoding:NSUTF8StringEncoding];
}else if ([json isKindOfClass:[NSData class]]) {
data = json;
}
if (data) {
dic = [NSJSONSerialization JSONObjectWithData:data
options:NSJSONReadingMutableLeaves error:nil];
if (![dic isKindOfClass:[NSDictionary class]]) {
dic = nil;
}
}
return dic;
}
간단한 사전 회전 모형
//
+ (instancetype)modelWithDict:(NSDictionary *)dict{
id model = [[self alloc] init];
unsigned int count = 0;
//
Ivar *ivars = class_copyIvarList(self, &count);
for (int i = 0 ; i < count; i++) {
Ivar ivar = ivars[i];
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
//
ivarName = [ivarName substringFromIndex:1];
id value = dict[ivarName];
//nil
[model setValue:value forKeyPath:ivarName];
}
return model;
}
use
//
- (void)simpleness_jsonToModle{
Department *dp = [[Department alloc] init];
NSDictionary *dict = @{
@"employeeId" : @2,
@"name" : @"Emp2",
@"department" : dp
};
Employee *el = [Employee modelWithDict:dict];
NSLog(@"%@",el.name);
}
이렇게 하면 사전을 간단하게 모델로 바꿀 수 있지만 몇 가지 단점이 있다. 확장성이 낮고 메모리 소모가 크며 속성 대상과 연결할 수 없고 이상하게 처리되지 않는다는 것이다.그러나 이 간단한 전환을 통해 JSON에서 모델로 비치는 원리를 알 수 있다. 바로 글의 첫머리에서 말한 바와 같다.
최적화
마지막으로 KVC를 통해 속성에 값을 부여해야 하기 때문에 속성 목록을 가져옵니다.
//
+ (NSArray *)objectProperties {
static const void * const kPropertyKey = &kPropertyKey;
//runtime
NSMutableArray *properties = objc_getAssociatedObject(self, kPropertyKey);
if (!properties) {
properties = [NSMutableArray array];
unsigned int count;
//
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i=0; i%@", [NSString stringWithUTF8String:propertyName]);
[properties addObject:[NSString stringWithUTF8String:propertyName]];
}
objc_setAssociatedObject(self, kPropertyKey, properties, OBJC_ASSOCIATION_COPY);
}
return properties;
}
하나의 매거진으로 속성 유형을 사용자 정의하여 어떤 속성 대상이 관련되어야 하는지를 판단하다
typedef NS_ENUM(NSUInteger, WWPropertyType) {
WWPropertyTypeUndefined,
WWPropertyTypeInteger,
WWPropertyTypeFloat,
WWPropertyTypeString,
WWPropertyTypeBoolean,
WWPropertyTypeDate,
WWPropertyTypeData,
WWPropertyTypeArray,
WWPropertyTypeRelationship
};
//
+ (NSDictionary *)propertyTypes {
static const void * const kPropertyTypesKey = &kPropertyTypesKey;
//runtime
NSDictionary *result = objc_getAssociatedObject(self, kPropertyTypesKey);
if (!result) {
NSArray *properties = [self objectProperties];
result = [[NSMutableDictionary alloc] initWithCapacity:properties.count];
for (NSString *property in properties) {
WWPropertyType type = [self propertyTypeOfClass:self propertyName:property];
[(NSMutableDictionary *)result setObject:@(type) forKey:property];
}
objc_setAssociatedObject(self, kPropertyTypesKey, result, OBJC_ASSOCIATION_COPY);
}
return result;
}
사용자 정의 속성 유형
//
+ (WWPropertyType)propertyTypeOfClass:(Class)classType propertyName:(NSString *)propertyName {
//
NSString *type = [WWReflection ww_propertyTypeOfClass:classType propertyName:propertyName];
if ([@"int" isEqualToString:type] ||
[@"unsigned" isEqualToString:type] ||
[@"short" isEqualToString:type] ||
[@"long" isEqualToString:type] ||
[@"unsigned long" isEqualToString:type] ||
[@"long long" isEqualToString:type] ||
[@"unsigned long long" isEqualToString:type] ||
[@"char" isEqualToString:type]) {
return WWPropertyTypeInteger;
} else if ([@"float" isEqualToString:type] ||
[@"double" isEqualToString:type]) {
return WWPropertyTypeFloat;
} else if ([@"NSString" isEqualToString:type] ||
[@"NSMutableString" isEqualToString:type]) {
return WWPropertyTypeString;
} else if ([@"bool" isEqualToString:type]) {
return WWPropertyTypeBoolean;
} else if ([@"NSDate" isEqualToString:type]) {
return WWPropertyTypeDate;
} else if ([@"NSData" isEqualToString:type] ||
[@"NSMutableData" isEqualToString:type]) {
return WWPropertyTypeData;
} else if ([@"NSArray" isEqualToString:type] ||
[@"NSMutableArray" isEqualToString:type]) {
return WWPropertyTypeArray;
}
else {
Class propertyClass = NSClassFromString(type);
//
if ([propertyClass isSubclassOfClass:[NSObject class]]) {
return WWPropertyTypeRelationship;
}
return WWPropertyTypeUndefined;
}
}
속성의 데이터 유형을 가져오는 방법도 필요합니다.
+ (NSString *)ww_propertyTypeOfClass:(Class)classType propertyName:(NSString *)propertyName {
static NSMutableDictionary *cache = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
cache = [[NSMutableDictionary alloc] init];
});
@synchronized(cache) {
NSMutableDictionary *propertyTypeMap = [cache objectForKey:NSStringFromClass(classType)];
if (!propertyTypeMap) {
propertyTypeMap = [[NSMutableDictionary alloc] init];
[cache setObject:propertyTypeMap forKey:NSStringFromClass(classType)];
}
NSString *type = [propertyTypeMap objectForKey:propertyName];
if (!type) {
objc_property_t property = class_getProperty(classType, [propertyName UTF8String]);
NSString *attributes = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
if ([attributes hasPrefix:@"T@"]) {
NSArray *substrings = [attributes componentsSeparatedByString:@"\""];
if ([substrings count] >= 2) {
type = [substrings objectAtIndex:1];
} else {
type = @"id";
}
} else if ([attributes hasPrefix:@"T{"]) {
type = @"struct";
} else {
if ([attributes hasPrefix:@"Ti"]) {
type = @"int";
} else if ([attributes hasPrefix:@"TI"]) {
type = @"unsigned";
} else if ([attributes hasPrefix:@"Ts"]) {
type = @"short";
} else if ([attributes hasPrefix:@"Tl"]) {
type = @"long";
} else if ([attributes hasPrefix:@"TL"]) {
type = @"unsigned long";
} else if ([attributes hasPrefix:@"Tq"]) {
type = @"long long";
} else if ([attributes hasPrefix:@"TQ"]) {
type = @"unsigned long long";
} else if ([attributes hasPrefix:@"TB"]) {
type = @"bool";
} else if ([attributes hasPrefix:@"Tf"]) {
type = @"float";
} else if ([attributes hasPrefix:@"Td"]) {
type = @"double";
} else if ([attributes hasPrefix:@"Tc"]) {
type = @"char";
} else if ([attributes hasPrefix:@"T^i"]) {
type = @"int *";
} else if ([attributes hasPrefix:@"T^I"]) {
type = @"unsigned *";
} else if ([attributes hasPrefix:@"T^s"]) {
type = @"short *";
} else if ([attributes hasPrefix:@"T^l"]) {
type = @"long *";
} else if ([attributes hasPrefix:@"T^q"]) {
type = @"long long *";
} else if ([attributes hasPrefix:@"T^Q"]) {
type = @"unsigned long long *";
} else if ([attributes hasPrefix:@"T^B"]) {
type = @"bool *";
} else if ([attributes hasPrefix:@"T^f"]) {
type = @"float *";
} else if ([attributes hasPrefix:@"T^d"]) {
type = @"double *";
} else if ([attributes hasPrefix:@"T*"]) {
type = @"char *";
} else {
NSAssert(0, @"Unkonwn type");
}
}
[propertyTypeMap setObject:type forKey:propertyName];
}
return type;
}
}
마지막 귀속 호출
+ (instancetype)ww_objectWithDictionary:(NSDictionary *)dictionary {
id object = [[self alloc] init];
NSArray *objectProperties = [self objectProperties];
NSDictionary *propertyTypes = [self propertyTypes];
for (NSString *key in dictionary.allKeys) {
if ([objectProperties containsObject:key]) {
id value = [dictionary objectForKey:key];
WWPropertyType propertyType = [[propertyTypes objectForKey:key] unsignedIntegerValue];
//
if (propertyType == WWPropertyTypeRelationship) {
NSAssert([value isKindOfClass:[NSDictionary class]], @"");
Class propertyClass = [[self propertyClasses] objectForKey:key];
value = [propertyClass ww_objectWithDictionary:value];
}
[object setValue:value forKey:key];
}
}
return object;
}
우리는 또 방법을 교환하여 호출할 수 있다
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL simpleness_Sel = @selector(simpleness_jsonToModle);
SEL complex_Sel = @selector(complex_dicToObject);
// Method
Method simpleMethod = class_getInstanceMethod([self class], simpleness_Sel);
Method complexMethod = class_getInstanceMethod([self class], complex_Sel);
// , ,
BOOL isAdd = class_addMethod([self class], simpleness_Sel, method_getImplementation(complexMethod), method_getTypeEncoding(complexMethod));
if (isAdd) {
// ,
//
class_replaceMethod([self class], simpleness_Sel, method_getImplementation(simpleMethod), method_getTypeEncoding(simpleMethod));
}else{
// ,
method_exchangeImplementations(simpleMethod, complexMethod);
}
});
// complex_dicToObject
[self simpleness_jsonToModle];
// [self complex_dicToObject];
}
이렇게 소개하는 것은 비교적 난잡할 수 있으니 다운로드할 수 있고 코드를 대조해 보면 그래도 비교적 간단하다.전체 소스 주소
물론 이것은 런타임 지식을 간단하게 활용하는 것일 뿐입니다. 만약에 더 좋고 효율적인 json 회전 모델을 사용하고 싶다면GitHub의 YY모델을 참고하십시오.
만약 네가 여기까지 보았다면 나에게 좋아요를 눌러라. 너의 좋아요는 내가 억지를 부리는 (아아, 오리지널을 견지하는) 무진장한 동력이다.
그리고......
제 소원은...
세계 평화...
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.