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

  1. Decode YYModel (a) basis
  2. Decode YYModel (two) features
  3. Decode YYModel (three) reference