Following up, this article provides a guide to some of the runtime methods used in YYModel and other code snippets:
Continue using the class YYMessage as follows:
@interface YYMessage : NSObject @property (nonatomic, assign) uint64_t messageId; @property (nonatomic, strong) NSString *content; @property (nonatomic, strong) NSDate *time; @property (nonatomic ,copy) NSString *name; @end @implementation YYMessage + (NSDictionary *)modelCustomPropertyMapper { return @{@"messageId":@[@"id", @"ID", @"mes_id"], @"time":@"t", @"name":@"user.name" }; } - (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic { uint64_t timestamp = [dic unsignedLongLongValueForKey:@"t" default:0]; Self. Time = [NSDate dateWithTimeIntervalSince1970: timestamp / 1000.0]; return YES; } - (void)modelCustomTransformToDictionary:(NSMutableDictionary *)dic { dic[@"t"] = @([self.time timeIntervalSince1970] * 1000).description; } @endCopy the code
Class
class
class
What is a class object?
YYMessage
instance class is YYMessage
Class cls = [self class];
Copy the code
class_isMetaClass
Is it a metaclass?
YYMessage
instance isn’t meta class
_isMeta = class_isMetaClass(cls); //_isMeta = NO;
Copy the code
class_getName
Get the name of the class
YYMessage
instance name is “YYMessage”
_name = class_getName(cls); //_name is "YYMessage"
Copy the code
objc_getMetaClass
Gets a metaclass object
YYMessage
instance metaCls is YYMessage
_metaCls = objc_getMetaClass(class_getName(cls)); //_metaCls is YYMessage
Copy the code
class_getSuperclass
Access to the parent class
YYMessage
super class is NSObject
_superCls = class_getSuperclass(cls); //_superCls is NSObject
Copy the code
Method
The message
class_copyMethodList
Get message array
//methodCount = 11 unsigned int methodCount = 0; Method *methods = class_copyMethodList(cls, &methodCount);Copy the code
The following methods are based on the value of this method:
- (void)setMessageId:(uint64_t)messageId;
Copy the code
method_getName
Get method name
//(SEL) _sel = "setMessageId:"
_sel = method_getName(method);
Copy the code
method_getImplementation
Method SEL
//(IMP) _imp = 0x00000001054d6210 (YYKitDemo`-[YYMessage setMessageId:] at YYModelExample.m:133)
_imp = method_getImplementation(method);
Copy the code
sel_getName
Obtain the SEL name
//name = "modelCustomTransformFromDictionary:"
const char *name = sel_getName(_sel);
Copy the code
method_getTypeEncoding
Gets the type encoding of the method
//- (voide)setMessageId:(uint64_t)messageID
//typeEncoding = v24@0:8Q16
const char *typeEncoding = method_getTypeEncoding(method);
Copy the code
method_copyReturnType
Gets the type Encoding of the return value of the method
//- (voide)setMessageId:(uint64_t)messageID
//_returnTypeEncoding = "v"
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
Copy the code
method_getNumberOfArguments
Gets the number of arguments to a method call
//- (voide)setMessageId (uint64_t)messageID // argumentCount = 3 // self, sel, messageID unsigned int argumentCount = method_getNumberOfArguments(method);Copy the code
method_copyArgumentType
Reference: Build ios-Model layer (2) type resolution
//- (voide)setMessageId:(uint64_t)messageID // self = id type, argumentType = @ "@" // sel type, argumentType = @ ":" //uint64_t type, ArgumentType = @" Q "char *argumentType = method_copyArgumentType(method, I);Copy the code
Property
attribute
class_copyPropertyList
Get an array of properties
// Properties array // propertyCount = 4 unsigned int propertyCount = 0; objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);Copy the code
The following values are based on this attribute:
[@property ](/property ) (nonatomic, assign) uint64_t messageId;
Copy the code
property_getName
Get attribute name
//name = "messageId"
const char *name = property_getName(property);
Copy the code
property_copyAttributeList
Gets an array of attributes
// attrCount = 3,
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
Copy the code
name | value | instructions | |
---|---|---|---|
attrs[0] | “T” | “Q” | Specifies the type using old-style encoding |
attrs[1] | “N” | “” | nonatomic |
attrs[2] | “V” | _messageId” | The instance variables |
Ivar
The instance variables
class_copyIvarList
Gets an array of instance variables
// ivars is an array of instance variables unsigned int ivarCount = 0; Ivar *ivars = class_copyIvarList(cls, &ivarCount);Copy the code
The following values are based on _messageId:
ivar_getName
Get the variable name:
//name = "_messageId"
const char *name = ivar_getName(ivar);
Copy the code
ivar_getOffset
Get the offset position of the variable in the class object:
Ptrdiff_t Like size_t,ptrdiff_t is a machine-specific type defined in the CSTddef header. Size_t is an unsigned type, while ptrdiff_t is a signed integer. // _offset = 8 ptrdiff_t _offset = ivar_getOffset(ivar); // If it is _content, _offset = 16Copy the code
ivar_getTypeEncoding
Gets the type encoding of the variable
//typeEncoding = "Q" const char *typeEncoding = ivar_getTypeEncoding(ivar); TypeEncoding = @ "NSString"Copy the code
NSObject
NSObject is an interesting class. Let’s look at some of its features through the above analysis:
_cls: NSObject _superCls: nil _metaCls: NSObject _isMeta: NO methodCount: 1328 propertyCount: 49 ivarCount: 1 ivar: There is only one instance variable, isa > ISA typeEncoding: #, offset: 0Copy the code
NSDate parsing
YYModel uses block to parse string to date, which is very clever. Here are the supported formats:Copy the code
format | The sample |
---|---|
yyyy-MM-dd | 2014-01-20 |
yyyy-MM-dd HH:mm:ss | The 2014-01-20 12:24:48/2014-01-20 T12:24:48. 000 |
yyyy-MM-dd’T’HH:mm:ss | The 2014-01-20 T12:24:48 / T12:2014-01-20 24:48. 000 |
yyyy-MM-dd’T’HH:mm:ssZ | 2014-01-20T12:24:48Z/ 2014-01-20T12:24:48+0800/ 2014-01-20T12:24:48+12:00 |
yyyy-MM-dd’T’HH:mm:ss.SSSZ | The 2014-01-20 T12:24:48. 000 + 0800/2014-01-20 T12:24:48. 12:00 000 + / 2014-01-20 T12:24:48. 000 z |
EEE MMM dd HH:mm:ss Z yyyy | Fri Sep 04 00:12:21 +0800 2015 |
EEE MMM dd HH:mm:ss.SSS Z yyyy | Fri Sep 04 00:12:21.000 +0800 2015 |
Given the format list above, it's easy to think of reading String length and doing a bunch of if/else. As we analyzed in the last article, if/else is an inefficient case of traversal. We should avoid that. To see how 'YYModel' can be avoided, the simplified code is as follows:Copy the code
/// Parse string to date. static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) { typedef NSDate* (^YYNSDateParseBlock)(NSString *string); #define kParserNum 34 static YYNSDateParseBlock blocks[kParserNum + 1] = {0}; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0]; formatter.dateFormat = @"yyyy-MM-dd"; blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; }; } { formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss"; formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss"; formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS"; formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS"; blocks[19] = ^(NSString *string) { if ([string characterAtIndex:10] == 'T') { return [formatter1 dateFromString:string]; } else { return [formatter2 dateFromString:string]; }}; blocks[23] = ...... } { formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ"; formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ"; blocks[20] = ..... blocks[24] = ..... blocks[25] = ..... blocks[28] = ..... blocks[29] = ..... } { formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy"; blocks[30] = ..... blocks[34] = ..... }}); if (! string) return nil; if (string.length > kParserNum) return nil; YYNSDateParseBlock parser = blocks[string.length]; if (! parser) return nil; return parser(string); #undef kParserNum }Copy the code
We saw that before we did the transformation, we mapped the length<->block, and as soon as we got the string length, passed it to the block, we could return an NSDate object. The mapper relation of length<->block directly changes the conditional branch judgment into table lookup, which improves the efficiency.Copy the code
copy
In order to implement the copy protocol, the type processing in 'YYModel' :Copy the code
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class]; if (modelMeta->_nsType) return [self copy]; If (propertyMeta->_isCNumber) {case YYEncodingTypeBool: { bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); ((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num); } break; } else { switch (propertyMeta->_type & YYEncodingTypeMask) { case YYEncodingTypeObject: case YYEncodingTypeClass: case YYEncodingTypeBlock: { id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value); } break; case YYEncodingTypeSEL: case YYEncodingTypePointer: case YYEncodingTypeCString: { size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); ((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value); } break; case YYEncodingTypeStruct: case YYEncodingTypeUnion: { @try { NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)]; if (value) { [one setValue:value forKey:propertyMeta->_name]; } } @catch (NSException *exception) {} } // break; commented for code coverage in next line default: break; }}Copy the code
Except for the C number type, you get it directly from the getter and then the setter, the type is property consistent.Copy the code
// If property is a bool, Bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);Copy the code
In addition, three types need to be distinguished, which are:Copy the code
//id, Class, Block type id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); //void*, char*, SEL size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter); / / struct, union NSValue * value = [self valueForKey: NSStringFromSelector (propertyMeta - > _getter)];Copy the code
other
YYModel implements NSCoding protocol, hash and equal method, which can be further referenced.
A series of
- Decode YYModel (a) basis
- Decode YYModel (two) features
- Decode YYModel (three) reference