直接上代码,注意该段代码的调用者是属性来源所指的对象
- (BOOL)copyValueToInstance:(nonnull __kindof NSObject *)obj
bySingleProtocol:(nonnull Protocol *)protocol
whiteList:(nullable NSArray<NSString *> *)whiteList
blackList:(nullable NSArray<NSString *> *)blackList
{
if (!protocol) {
#if DEBUG
NSAssert(NO, @"protocol can't be nil");
#endif
return NO;
}
if (![self conformsToProtocol:protocol]) {
#if DEBUG
NSAssert(NO, @"make sure [self conformsToProtocol:protocol] be YES");
#endif
return NO;
}
/// 获取协议中的属性
unsigned int outCount;
objc_property_t *propertyList;
// if (@available(iOS 10.0, *)) {
// //只有iOS 10 以上才支持获取optional属性
// propertyList = protocol_copyPropertyList2(protocol, &outCount, NO, YES);
// } else {
// 只能获取required的属性
propertyList = protocol_copyPropertyList(protocol, &outCount);
// }
NSMutableSet <NSString *> *propertyNamesFromProtocol = NSMutableSet.set;
for (unsigned int i = 0; i < outCount; i++) {
objc_property_t property = propertyList[i];
const char *propertyName = property_getName(property);
NSString *keyName = @(propertyName);
[propertyNamesFromProtocol addObject:keyName];
}
if (propertyList) {
free(propertyList);
}
/// 赋值
for (NSString *targetPropertyName in propertyNamesFromProtocol) {
SEL getSel = NSSelectorFromString(targetPropertyName);
if (![self respondsToSelector:getSel]) {
#if DEBUG
NSAssert(NO, @"make sure [self respondsToSelector:propertySel] be YES");
#endif
return NO;
}
NSString *name = targetPropertyName.copy;
if ([targetPropertyName hasPrefix:@"_"]) {
name = [name substringFromIndex:1];
}
if (whiteList && ![whiteList containsObject:name]) {
continue;
}
if (blackList && [blackList containsObject:name]) {
continue;
}
NSString *selectorName = nil;
if (name.length > 1) {
selectorName = [NSString stringWithFormat:@"set%@%@:",[name substringWithRange:NSMakeRange(0, 1)].uppercaseString,[name substringFromIndex:1]];
} else if (name.length == 1) {
selectorName = [NSString stringWithFormat:@"set%@:",[name substringWithRange:NSMakeRange(0, 1)].uppercaseString];
} else {
continue;
}
SEL setSel = NSSelectorFromString(selectorName);
if (![obj respondsToSelector:setSel]) {
#if DEBUG
NSAssert(NO, @"make sure [obj respondsToSelector:setSel] be YES");
#endif
return NO;
}
id value = [self valueForKey:targetPropertyName];
if (value) {
[obj setValue:value forKey:targetPropertyName];
} else {
IMP imp = [obj methodForSelector:setSel];
void (*func)(id, SEL,id) = (void *)imp;
func(obj, setSel,nil);
}
}
return YES;
}
下面来看一下将一个实例的属性赋给另一个实例,前提是保证被赋值的实例完全支持来源实例的属性,该方法是内联方法,直接调用
id makeRemoteModelToCoreDataModel(id remoteModel,Class remoteDataClass,NSManagedObject *coreDataModel) {
int i;
unsigned int propertyCount = 0;
objc_property_t *propertyList = class_copyPropertyList(remoteDataClass, &propertyCount);
NSMutableArray *propertyNameList = [NSMutableArray array];
for ( i=0; i < propertyCount; i++ ) {
objc_property_t *thisProperty = propertyList + i;
const char* propertyName = property_getName(*thisProperty);
NSString *string = [NSString stringWithFormat:@"%s",propertyName];
[propertyNameList addObject:string];
}
if (propertyNameList.count > 0) {
[propertyNameList enumerateObjectsUsingBlock:^(NSString* key, NSUInteger idx, BOOL *stop) {
id obj = [remoteModel valueForKey:key];
if (obj) {
if ([obj isKindOfClass:[NSString class]]) {
[coreDataModel setValue:obj forKey:key];
}else {
NSData *objData = [NSKeyedArchiver archivedDataWithRootObject:obj];
[coreDataModel setValue:objData forKey:key];
}
}
}];
free(propertyList);
return coreDataModel;
}else return nil;
}