FrameWork Learning - Mantle
Mantle 소스 코드 의 가장 주요 한 내용 은:
여기 서 이 세 가 지 를 우리 의 분석 점 으로 삼 는 다.
기본 MTLModel
MTLModel 은 추상 적 인 유형 으로 대상 의 초기 화 와 압축 파일 작업 을 처리 하 는 기본 적 인 행 위 를 제공 합 니 다.
초기 화
MTLModel 의 기본 초기 화 방법 - init 는 아무 일 도 하지 않 고 [슈퍼 init] 만 호출 했 습 니 다.동시에 다른 초기 화 방법 을 제공 합 니 다.
- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;
그 중에서 매개 변수 dictionary Value 는 사전 으로 대상 을 초기 화 하 는 key - value 쌍 을 포함 합 니 다.우 리 는 그것 의 구체 적 인 실현 을 살 펴 보 자.
- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
...
for (NSString *key in dictionary) {
// 1. value __autoreleasing, MTLValidateAndSetValue ,
//
__autoreleasing id value = [dictionary objectForKey:key];
// 2. value NSNull.null, nil
if ([value isEqual:NSNull.null]) value = nil;
// 3. MTLValidateAndSetValue KVC value key ,
// , key 。
// KVC value model key 。
// MTLValidateAndSetValue , 。
BOOL success = MTLValidateAndSetValue(self, key, value, YES, error);
if (!success) return nil;
}
...
}
하위 클래스 는 이 방법 을 다시 쓸 수 있 습 니 다. 대상 의 속성 을 설정 한 후에 진일보 한 처리 나 초기 화 작업 을 할 수 있 습 니 다. 그러나 기억 해 야 할 것 은 슈퍼 를 통 해 부모 클래스 의 실현 을 호출해 야 한 다 는 것 입 니 다.
속성의 키 (key), 값 (value) 가 져 오기
MTLModel 클래스 는 클래스 방법 + propertyKeys 를 제공 합 니 다. 이 방법 은 모든 @ property 성명 의 속성 에 대응 하 는 이름 문자열 의 집합 을 되 돌려 줍 니 다. 단, 속성 과 MTLModel 자체 의 속성 만 읽 는 것 은 포함 되 지 않 습 니 다.이 방법 은 model 의 모든 속성 을 옮 겨 다 닙 니 다. 속성 이 읽 기만 하고 ivar 값 이 NULL 이 아니라면 속성 명 을 나타 내 는 문자열 을 가 져 와 집합 에 넣 습 니 다. 이 는 다음 과 같 습 니 다.
+ (NSSet *)propertyKeys {
// 1. , 。 。
NSSet *cachedKeys = objc_getAssociatedObject(self, MTLModelCachedPropertyKeysKey);
if (cachedKeys != nil) return cachedKeys;
NSMutableSet *keys = [NSMutableSet set];
// 2.
// enumeratePropertiesUsingBlock superclass MTLModel,
// model ( MTLModel), block 。
// enumeratePropertiesUsingBlock , 。
[self enumeratePropertiesUsingBlock:^(objc_property_t property, BOOL *stop) {
mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property);
@onExit {
free(attributes);
};
// 3. ivar NULL
if (attributes->readonly && attributes->ivar == NULL) return;
// 4. ,
NSString *key = @(property_getName(property));
[keys addObject:key];
}];
// 5. 。
objc_setAssociatedObject(self, MTLModelCachedPropertyKeysKey, keys, OBJC_ASSOCIATION_COPY);
return keys;
}
위 와 같은 방법 이 있 습 니 다. 대상 의 모든 속성 과 해당 하 는 값 을 얻 으 려 면 방법 입 니 다.이 를 위해 MTLModel 은 현재 model 의 모든 속성 과 값 을 포함 하 는 사전 을 읽 기 전용 속성 dictionary Value 를 제공 합 니 다.속성 값 이 nil 이면 NSNull 로 대체 합 니 다.또한 이 속성 은 nil 이 아 닙 니 다.
@property (nonatomic, copy, readonly) NSDictionary *dictionaryValue;
//
- (NSDictionary *)dictionaryValue {
return [self dictionaryWithValuesForKeys:self.class.propertyKeys.allObjects];
}
병합 대상
합병 대상 은 두 MTLModel 대상 을 사용자 정의 방법 에 따라 대응 하 는 속성 값 을 합 치 는 것 을 말한다.이 를 위해 MTLModel 에서 다음 과 같은 방법 을 정의 했다.
- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model;
이 방법 은 현재 대상 이 지정 한 key 속성의 값 과 model 매개 변수 에 대응 하 는 속성 값 을 지정 한 규칙 에 따라 통합 합 니 다. 이 규칙 은 우리 가 사용자 정의 - merge FromModel: 방법 으로 확정 합 니 다.만약 우리 의 하위 클래스 에서 - merge FromModel: 방법 을 실현 한다 면 그것 을 호출 할 것 입 니 다.찾 지 못 하고 model 이 nil 이 아니라면 model 의 속성 값 으로 현재 대상 의 속성 값 을 대체 합 니 다.구체 적 인 실현 은 다음 과 같다.
- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model {
NSParameterAssert(key != nil);
// 1. key "mergeFromModel:" , selector
// -mergeFromModel: , model nil, model
//
//
// MTLSelectorWithCapitalizedKeyPattern C ,
// ,
SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:");
if (![self respondsToSelector:selector]) {
if (model != nil) {
[self setValue:[model valueForKey:key] forKey:key];
}
return;
}
// 2. NSInvocation -mergeFromModel: 。
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
invocation.target = self;
invocation.selector = selector;
[invocation setArgument:&model atIndex:2];
[invocation invoke];
}
그 밖 에 MTLModel 은 두 대상 의 모든 속성 치 를 합병 하 는 또 다른 방법 을 제공 했다. 즉,:
- (void)mergeValuesForKeysFromModel:(MTLModel *)model;
주의해 야 할 것 은 model 은 현재 대상 이 속 한 클래스 나 하위 클래스 여야 합 니 다.
압축 파일 대상 (아 카 이브)
맨틀 은 MTLModel 에 대한 인 코딩 디 코딩 처 리 를 MTLModel 의 NSCoding 분류 에 넣 어 처 리 했 으 며, 이 분류 및 관련 정 의 는 MTLModel + NSCoding 파일 에 담 았 다.
서로 다른 속성 에 대해 인 코딩 디 코딩 과정 에서 차별 화 될 수 있 습 니 다. 이 를 위해 Mentle 은 MTLModel Encoding Behavior 를 매 거 하여 MTLModel 속성 이 압축 파일 에 인 코딩 된 행 위 를 확인 합 니 다.그 정 의 는 다음 과 같다.
typedef enum : NSUInteger {
MTLModelEncodingBehaviorExcluded = 0, //
MTLModelEncodingBehaviorUnconditional, //
MTLModelEncodingBehaviorConditional, // 。
} MTLModelEncodingBehavior;
모든 속성의 압축 파일 행 위 를 구체 적 으로 설정 할 수 있 습 니 다.MTLModel 류 는 우리 에 게 기본 적 인 실현 을 제공 합 니 다. 다음 과 같 습 니 다.
+ (NSDictionary *)encodingBehaviorsByPropertyKey {
// 1.
NSSet *propertyKeys = self.propertyKeys;
NSMutableDictionary *behaviors = [[NSMutableDictionary alloc] initWithCapacity:propertyKeys.count];
// 2.
for (NSString *key in propertyKeys) {
objc_property_t property = class_getProperty(self, key.UTF8String);
NSAssert(property != NULL, @"Could not find property \"%@\" on %@", key, self);
mtl_propertyAttributes *attributes = mtl_copyPropertyAttributes(property);
@onExit {
free(attributes);
};
// 3. weak , MTLModelEncodingBehaviorConditional, MTLModelEncodingBehaviorUnconditional, , NSNumber 。
MTLModelEncodingBehavior behavior = (attributes->weak ? MTLModelEncodingBehaviorConditional : MTLModelEncodingBehaviorUnconditional);
behaviors[key] = @(behavior);
}
return behaviors;
}
이 되 돌아 오지 않 는 사전 의 속성 은 압축 되 지 않 습 니 다.하위 클래스 는 자신의 수요 에 따라 각 속성의 압축 파일 행 위 를 지정 할 수 있 습 니 다.그러나 실제 적 으로 슈퍼 를 통 해 부류 의 실현 을 호출해 야 한다.
압축 파일 에서 지정 한 속성 을 디 코딩 하기 위해 Mantle 은 다음 과 같은 방법 을 제공 합 니 다.
- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion;
기본적으로 이 방법 은 현재 대상 에서 - decodeWith Coder: model 버 전과 유사 한 방법 을 찾 습 니 다. 찾 으 면 해당 방법 을 호출 하고 사용자 정의 방식 으로 속성 디 코딩 을 처리 합 니 다.사용자 정의 방법 이나 coder 가 보안 인 코딩 이 필요 하지 않 으 면 지정 한 key 호출 - [NSCoder decodeObject ForKey:] 방법 을 사용 합 니 다.그 구체 적 인 실현 은 다음 과 같다.
- (id)decodeValueForKey:(NSString *)key withCoder:(NSCoder *)coder modelVersion:(NSUInteger)modelVersion {
...
SEL selector = MTLSelectorWithCapitalizedKeyPattern("decode", key, "WithCoder:modelVersion:");
// 1. -decodeWithCoder:modelVersion: , NSInvocation
if ([self respondsToSelector:selector]) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:selector]];
invocation.target = self;
invocation.selector = selector;
[invocation setArgument:&coder atIndex:2];
[invocation setArgument:&modelVersion atIndex:3];
[invocation invoke];
__unsafe_unretained id result = nil;
[invocation getReturnValue:&result];
return result;
}
@try {
// 2. -decodeWithCoder:modelVersion: ,
// 。
//
// coderRequiresSecureCoding
if (coderRequiresSecureCoding(coder)) {
// 3. coder , ,
// 。
// ,MTLModel allowedSecureCodingClassesByPropertyKey,
// 。
// , 。 ,
// , , NSValue,
// , 。
// 。
NSArray *allowedClasses = self.class.allowedSecureCodingClassesByPropertyKey[key];
NSAssert(allowedClasses != nil, @"No allowed classes specified for securely decoding key \"%@\" on %@", key, self.class);
return [coder decodeObjectOfClasses:[NSSet setWithArray:allowedClasses] forKey:key];
} else {
// 4.
return [coder decodeObjectForKey:key];
}
} @catch (NSException *exception) {
...
}
}
물론 모든 인 코딩 디 코딩 작업 은 - init With Coder: 와 - encodeWith Coder: 두 가지 방법 으로 이 루어 져 야 합 니 다.우 리 는 MTLModel 의 하위 클래스 를 정의 할 때 자신의 수요 에 따라 특정한 속성 을 처리 할 수 있 지만 슈퍼 의 실현 을 호출 하여 부모 클래스 의 작업 을 수행 하 는 것 이 좋 습 니 다.MTLModel 은 이 두 가지 방법의 실현 에 대해 소스 코드 를 참고 하 시기 바 랍 니 다. 여기 서 설명 을 많이 하지 않 습 니 다.
어댑터 MTLJSONapadter
MTLModel 대상 과 JSON 사전 간 의 상호 전환 을 편리 하 게 하기 위해 Mantle 은 이러한 MTLJSONapadter 를 제공 하여 이 두 가지 어댑터 로 한다.
MTLJSONserializing 프로 토 콜
Mantle 은 JSON 사전 과 상호 전환 이 필요 한 MTLModel 의 하위 클래스 가 MTLJSONserializing 을 실현 하여 MTLJSONapadter 대상 의 전환 을 편리 하 게 해 야 한다 고 정의 했다.이 협의 에서 세 가지 방법 을 정 의 했 는데 구체 적 으로 다음 과 같다.
@protocol MTLJSONSerializing
@required
+ (NSDictionary *)JSONKeyPathsByPropertyKey;
@optional
+ (NSValueTransformer *)JSONTransformerForKey:(NSString *)key;
+ (Class)classForParsingJSONDictionary:(NSDictionary *)JSONDictionary;
@end
이 세 가지 방법 은 모두 같은 방법 이다.그 중에서 + JSONkey Pathsby Property Key 는 반드시 이 루어 져 야 합 니 다. 되 돌아 오 는 사전 은 대상 의 속성 을 JSON 의 서로 다른 key path (문자열 값 이나 NSNull) 에 어떻게 표시 하 는 지 지정 합 니 다.이 사전 에 없 는 속성 은 JSON 에서 사용 하 는 key 값 과 일치 하 는 것 으로 여 겨 집 니 다.NSNull 에 비 친 속성 은 JSON 서열 화 과정 에서 처리 되 지 않 습 니 다.
+ JSONtransformerForKey: 방법 은 JSON 값 을 지정 한 속성 값 으로 변환 하 는 방법 을 지정 합 니 다.반대로 변환기 도 속성 값 을 JSON 값 으로 변환 하 는 데 사용 된다.변환기 가 + JSONtransformer 방법 을 실현 하면 MTLJSONadapter 는 이 구체 적 인 방법 을 사용 하고 + JSONtransformerForKey: 방법 을 사용 하지 않 습 니 다.또한 사용자 정의 변환 이 필요 하지 않 으 면 nil 로 돌아 갑 니 다.
재 작성 + classForParsingJSONdictionary: 방법 은 현재 모델 을 다른 클래스 의 대상 으로 해석 할 수 있 습 니 다.이 대상 클래스 는 매우 유용 합 니 다. 그 중에서 추상 적 인 기 류 는 - [MTLJSONadapter initWithJSONdictionary: model Class:] 방법 에 전달 되 고 실례 화 된 것 은 하위 클래스 입 니 다.
만약 우리 가 MTLModel 의 하위 클래스 가 MTLJSONapadter 를 사용 하여 전환 할 수 있 기 를 원한 다 면 이 협 의 를 실현 하고 해당 하 는 방법 을 실현 해 야 한다.
초기 화
MTLJSONapadter 대상 은 읽 기 전용 속성 이 있 습 니 다. 이 속성 은 어댑터 가 처리 해 야 할 MTLModel 대상 입 니 다. 그 성명 은 다음 과 같 습 니 다.
@property (nonatomic, strong, readonly) MTLModel
*model;
이 대상 은 MTLJSONserializing 합 의 를 실현 한 MTLModel 대상 이 어야 한 다 는 것 을 알 수 있다.이 속성 은 읽 기 전용 이기 때문에 초기 화 방법 으로 만 초기 화 할 수 있 습 니 다.
MTLJSONapadter 대상 은 - init 를 통 해 초기 화 할 수 없 으 며 이 방법 은 직접적 으로 단언 합 니 다.클래스 가 제공 하 는 두 가지 초기 화 방법 을 통 해 초기 화 해 야 합 니 다. 다음 과 같 습 니 다.
- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass error:(NSError **)error;
- (id)initWithModel:(MTLModel*)model;
그 중에서 - (id) initWithJSONdictionary: modelclass: error: 사전 과 변환 할 클래스 를 사용 하여 초기 화 합 니 다.사전 JSONdictionary 는 JSON 데 이 터 를 표시 합 니 다. 이 사전 은 NSJSONserialization 이 되 돌아 오 는 형식 에 부합 해 야 합 니 다.이 인자 가 비어 있 으 면 nil 을 되 돌려 주 고 MTLJSONadapter Error InvalidJSONdictionary 코드 가 있 는 error 대상 을 되 돌려 줍 니 다.이 방법의 구체 적 인 실현 은 다음 과 같다.
- (id)initWithJSONDictionary:(NSDictionary *)JSONDictionary modelClass:(Class)modelClass error:(NSError **)error {
...
if (JSONDictionary == nil || ![JSONDictionary isKindOfClass:NSDictionary.class]) {
...
return nil;
}
if ([modelClass respondsToSelector:@selector(classForParsingJSONDictionary:)]) {
modelClass = [modelClass classForParsingJSONDictionary:JSONDictionary];
if (modelClass == nil) {
...
return nil;
}
...
}
...
_modelClass = modelClass;
_JSONKeyPathsByPropertyKey = [[modelClass JSONKeyPathsByPropertyKey] copy];
NSMutableDictionary *dictionaryValue = [[NSMutableDictionary alloc] initWithCapacity:JSONDictionary.count];
NSSet *propertyKeys = [self.modelClass propertyKeys];
// 1. model +JSONKeyPathsByPropertyKey key-value
for (NSString *mappedPropertyKey in self.JSONKeyPathsByPropertyKey) {
// 2. model +JSONKeyPathsByPropertyKey
// nil。 +JSONKeyPathsByPropertyKey model
// 。
if (![propertyKeys containsObject:mappedPropertyKey]) {
...
return nil;
}
id value = self.JSONKeyPathsByPropertyKey[mappedPropertyKey];
// 3. JSON NSNull, nil。
if (![value isKindOfClass:NSString.class] && value != NSNull.null) {
...
return nil;
}
}
for (NSString *propertyKey in propertyKeys) {
NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey];
if (JSONKeyPath == nil) continue;
id value;
@try {
value = [JSONDictionary valueForKeyPath:JSONKeyPath];
} @catch (NSException *ex) {
...
return nil;
}
if (value == nil) continue;
@try {
// 4. ,
// ,+JSONTransformerForKey: +JSONTransformer ,
// , , +JSONTransformerForKey:
//
NSValueTransformer *transformer = [self JSONTransformerForKey:propertyKey];
if (transformer != nil) {
// 5.
if ([value isEqual:NSNull.null]) value = nil;
value = [transformer transformedValue:value] ?: NSNull.null;
}
dictionaryValue[propertyKey] = value;
} @catch (NSException *ex) {
...
return nil;
}
}
// 6. _model
_model = [self.modelClass modelWithDictionary:dictionaryValue error:error];
if (_model == nil) return nil;
return self;
}
또한 MTLJSONapadter 는 MTLJSONapadter 대상 을 만 드 는 몇 가지 방법 을 제공 했다. 다음 과 같다.
+ (id)modelOfClass:(Class)modelClass fromJSONDictionary:(NSDictionary *)JSONDictionary error:(NSError **)error;
+ (NSArray *)modelsOfClass:(Class)modelClass fromJSONArray:(NSArray *)JSONArray error:(NSError **)error;
+ (NSDictionary *)JSONDictionaryFromModel:(MTLModel*)model;
구체 적 으로 실현 하면 소스 코드 를 참고 할 수 있다.
대상 에서 JSON 데이터 가 져 오기
MTLModel 대상 에서 JSON 데 이 터 를 얻 는 것 은 상기 초기 화 과정 중의 역 과정 이다.이 과정 은 - JSONdictionary 방법 으로 이 루어 집 니 다. 구체 적 으로 다음 과 같 습 니 다.
- (NSDictionary *)JSONDictionary {
NSDictionary *dictionaryValue = self.model.dictionaryValue;
NSMutableDictionary *JSONDictionary = [[NSMutableDictionary alloc] initWithCapacity:dictionaryValue.count];
[dictionaryValue enumerateKeysAndObjectsUsingBlock:^(NSString *propertyKey, id value, BOOL *stop) {
NSString *JSONKeyPath = [self JSONKeyPathForPropertyKey:propertyKey];
if (JSONKeyPath == nil) return;
// 1.
NSValueTransformer *transformer = [self JSONTransformerForKey:propertyKey];
if ([transformer.class allowsReverseTransformation]) {
if ([value isEqual:NSNull.null]) value = nil;
value = [transformer reverseTransformedValue:value] ?: NSNull.null;
}
NSArray *keyPathComponents = [JSONKeyPath componentsSeparatedByString:@"."];
// 2. , keypath ,
// obj , ; ,
// : @{@"nested": @{@"name": @"foo"}}
id obj = JSONDictionary;
for (NSString *component in keyPathComponents) {
if ([obj valueForKey:component] == nil) {
[obj setValue:[NSMutableDictionary dictionary] forKey:component];
}
obj = [obj valueForKey:component];
}
[JSONDictionary setValue:value forKeyPath:JSONKeyPath];
}];
return JSONDictionary;
}
위 에서 알 수 있 듯 이 이 방법 은 실제로 하나의 사전 을 얻 었 다.사전 을 얻 은 뒤 JSON 꼬치 로 서열 화하 기 가 쉽다.
MTLJSONapadter 도 하나의 model 에서 JSON 사전 을 얻 을 수 있 는 간단 한 방법 을 제공 합 니 다. 그 정 의 는 다음 과 같 습 니 다.
+ (NSDictionary *)JSONDictionaryFromModel:(MTLModel
*)model;
MTLManagedObjectAdapter
코어 데이터 에 적응 하기 위해 맨틀 은 MTLManaged ObjectAdapter 류 를 전문 적 으로 정의 했다.이 종 류 는 MTLModel 대상 과 NSManaged Object 대상 이전의 전환 에 사용 된다.구체 적 인 것 은 여기 서 상세 하 게 설명 하지 않 겠 습 니 다.
기술 점 총화
Mantle 의 기능 은 주로 대상 간 의 데 이 터 를 전환 하 는 것 이다. 즉, MTLModel 과 JSON 사전 에서 데 이 터 를 어떻게 전환 하 는 지 하 는 것 이다.따라서 사용 하 는 기술 은 대부분 코코아 파운데이션 이 제공 하 는 기능 이다.Core Data 에 대한 처 리 를 제외 하고 주로 사용 하 는 기술 은 다음 과 같은 몇 가지 가 있 습 니 다.
KVC 의 응용: 이것 은 주로 MTLModel 하위 클래스 의 속성 할당 에 나타 나 고 KVC 체 제 를 통 해 값 의 유효성 을 검증 하고 속성 할당 에 나타난다.
NSValueTransform: 이것 은 주로 JSON 값 을 속성 값 으로 변환 하 는 데 사 용 됩 니 다. 우 리 는 변환 기 를 사용자 정의 하여 우리 자신의 변환 수 요 를 만족 시 킬 수 있 습 니 다.
NSInvocation: 이것 은 주로 특정한 key 값 에 대한 호출 을 통일 적 으로 처리 하 는 데 사 용 됩 니 다.예 를 들 어 - merge FromModel: 이런 방법.
Run time 함수 사용: 문자열 에서 방법 에 대응 하 는 문자열 을 가 져 온 다음 selregisterName 함수 로 selector 를 등록 합 니 다.
물론 Mantle 에 서 는 다른 기술 점 도 언급 되 어 서술 을 많이 하지 않 는 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Swift의 패스트 패스Objective-C를 대체하기 위해 만들어졌지만 Xcode는 Objective-C 런타임 라이브러리를 사용하기 때문에 Swift와 함께 C, C++ 및 Objective-C를 컴파일할 수 있습니다. Xcode는 S...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.