123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- //
- // NSObject+MJProperty.m
- // MJExtensionExample
- //
- // Created by MJ Lee on 15/4/17.
- // Copyright (c) 2015年 小码哥. All rights reserved.
- //
- #import "NSObject+MJProperty.h"
- #import "NSObject+MJKeyValue.h"
- #import "NSObject+MJCoding.h"
- #import "NSObject+MJClass.h"
- #import "MJProperty.h"
- #import "MJFoundation.h"
- #import <objc/runtime.h>
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wundeclared-selector"
- #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
- static const char MJReplacedKeyFromPropertyNameKey = '\0';
- static const char MJReplacedKeyFromPropertyName121Key = '\0';
- static const char MJNewValueFromOldValueKey = '\0';
- static const char MJObjectClassInArrayKey = '\0';
- static const char MJCachedPropertiesKey = '\0';
- dispatch_semaphore_t mje_signalSemaphore;
- dispatch_once_t mje_onceTokenSemaphore;
- @implementation NSObject (Property)
- + (NSMutableDictionary *)mj_propertyDictForKey:(const void *)key
- {
- static NSMutableDictionary *replacedKeyFromPropertyNameDict;
- static NSMutableDictionary *replacedKeyFromPropertyName121Dict;
- static NSMutableDictionary *newValueFromOldValueDict;
- static NSMutableDictionary *objectClassInArrayDict;
- static NSMutableDictionary *cachedPropertiesDict;
-
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary];
- replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary];
- newValueFromOldValueDict = [NSMutableDictionary dictionary];
- objectClassInArrayDict = [NSMutableDictionary dictionary];
- cachedPropertiesDict = [NSMutableDictionary dictionary];
- });
-
- if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict;
- if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict;
- if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict;
- if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict;
- if (key == &MJCachedPropertiesKey) return cachedPropertiesDict;
- return nil;
- }
- #pragma mark - --私有方法--
- + (id)mj_propertyKey:(NSString *)propertyName
- {
- MJExtensionAssertParamNotNil2(propertyName, nil);
-
- __block id key = nil;
- // 查看有没有需要替换的key
- if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) {
- key = [self mj_replacedKeyFromPropertyName121:propertyName];
- }
-
- // 调用block
- if (!key) {
- [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
- MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key);
- if (block) {
- key = block(propertyName);
- }
- if (key) *stop = YES;
- }];
- }
-
- // 查看有没有需要替换的key
- if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) {
- key = [self mj_replacedKeyFromPropertyName][propertyName];
- }
-
- if (!key || [key isEqual:propertyName]) {
- [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
- NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey);
- if (dict) {
- key = dict[propertyName];
- }
- if (key && ![key isEqual:propertyName]) *stop = YES;
- }];
- }
-
- // 2.用属性名作为key
- if (!key) key = propertyName;
-
- return key;
- }
- + (Class)mj_propertyObjectClassInArray:(NSString *)propertyName
- {
- __block id clazz = nil;
- if ([self respondsToSelector:@selector(mj_objectClassInArray)]) {
- clazz = [self mj_objectClassInArray][propertyName];
- }
-
- if (!clazz) {
- [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
- NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey);
- if (dict) {
- clazz = dict[propertyName];
- }
- if (clazz) *stop = YES;
- }];
- }
-
- // 如果是NSString类型
- if ([clazz isKindOfClass:[NSString class]]) {
- clazz = NSClassFromString(clazz);
- }
- return clazz;
- }
- #pragma mark - --公共方法--
- + (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration
- {
- // 获得成员变量
- NSArray *cachedProperties = [self mj_properties];
- // 遍历成员变量
- BOOL stop = NO;
- for (MJProperty *property in cachedProperties) {
- enumeration(property, &stop);
- if (stop) break;
- }
- }
- #pragma mark - 公共方法
- + (NSArray *)mj_properties
- {
- MJExtensionSemaphoreCreate
- MJ_LOCK(mje_signalSemaphore);
- NSMutableDictionary *cachedInfo = [self mj_propertyDictForKey:&MJCachedPropertiesKey];
- NSMutableArray *cachedProperties = cachedInfo[NSStringFromClass(self)];
- if (cachedProperties == nil) {
- cachedProperties = [NSMutableArray array];
-
- [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
- // 1.获得所有的成员变量
- unsigned int outCount = 0;
- objc_property_t *properties = class_copyPropertyList(c, &outCount);
-
- // 2.遍历每一个成员变量
- for (unsigned int i = 0; i<outCount; i++) {
- MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
- // 过滤掉Foundation框架类里面的属性
- if ([MJFoundation isClassFromFoundation:property.srcClass]) continue;
- // 过滤掉`hash`, `superclass`, `description`, `debugDescription`
- if ([MJFoundation isFromNSObjectProtocolProperty:property.name]) continue;
-
- property.srcClass = c;
- [property setOriginKey:[self mj_propertyKey:property.name] forClass:self];
- [property setObjectClassInArray:[self mj_propertyObjectClassInArray:property.name] forClass:self];
- [cachedProperties addObject:property];
- }
-
- // 3.释放内存
- free(properties);
- }];
-
- cachedInfo[NSStringFromClass(self)] = cachedProperties;
- }
- NSArray *properties = [cachedProperties copy];
- MJ_UNLOCK(mje_signalSemaphore);
-
- return properties;
- }
- #pragma mark - 新值配置
- + (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue {
- MJExtensionSemaphoreCreate
- MJ_LOCK(mje_signalSemaphore);
- objc_setAssociatedObject(self, &MJNewValueFromOldValueKey, newValueFormOldValue, OBJC_ASSOCIATION_COPY_NONATOMIC);
- MJ_UNLOCK(mje_signalSemaphore);
- }
- + (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(MJProperty *__unsafe_unretained)property{
- // 如果有实现方法
- if ([object respondsToSelector:@selector(mj_newValueFromOldValue:property:)]) {
- return [object mj_newValueFromOldValue:oldValue property:property];
- }
-
- MJExtensionSemaphoreCreate
- MJ_LOCK(mje_signalSemaphore);
- // 查看静态设置
- __block id newValue = oldValue;
- [self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
- MJNewValueFromOldValue block = objc_getAssociatedObject(c, &MJNewValueFromOldValueKey);
- if (block) {
- newValue = block(object, oldValue, property);
- *stop = YES;
- }
- }];
- MJ_UNLOCK(mje_signalSemaphore);
- return newValue;
- }
- + (void)mj_removeCachedProperties {
- MJExtensionSemaphoreCreate
- MJ_LOCK(mje_signalSemaphore);
- [[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects];
- MJ_UNLOCK(mje_signalSemaphore);
- }
- #pragma mark - array model class配置
- + (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray
- {
- [self mj_setupBlockReturnValue:objectClassInArray key:&MJObjectClassInArrayKey];
-
- [self mj_removeCachedProperties];
- }
- #pragma mark - key配置
- + (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName {
- [self mj_setupBlockReturnValue:replacedKeyFromPropertyName key:&MJReplacedKeyFromPropertyNameKey];
-
- [self mj_removeCachedProperties];
- }
- + (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 {
- MJExtensionSemaphoreCreate
- MJ_LOCK(mje_signalSemaphore);
- objc_setAssociatedObject(self, &MJReplacedKeyFromPropertyName121Key, replacedKeyFromPropertyName121, OBJC_ASSOCIATION_COPY_NONATOMIC);
-
- [[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects];
- MJ_UNLOCK(mje_signalSemaphore);
- }
- @end
- #pragma clang diagnostic pop
|