Study harmoniously! Don’t be impatient!! I’m your old friend, Xiao Qinglong

To undertake the article: OC’s Method_Swizling some pits, KVC principle analysis

preface

In the last article, we analyzed the principle of KVC. In this article, I will implement a customized KVC with you. I seem to have heard you Shouting: hurry up, don’t ink ~

The body of the

Come round:

// Setter principle:
1.In turn, to find the`set<Key>:``_set<Key>`, call the method when it is found; If not, enter the step2;2Make sure AccessInstanceVariables returns YES, then check in order`_<key>`.`_is<Key>`.`<key>`Or,`is<Key>`Set the variable with the input value when it is found. If not, enter the step3; (For example, find a search`_<key>`And then the back`_is<Key>`Wait so you don't need to look).3, call`setValue:forUndefinedKey:`And causeAbnormal ` `.// GetPrinciple:
1Get <Key>, <Key>, is<Key>, or _< Key>5; If not, enter the step2;2CountOf <Key> and objectIn<Key>AtIndex and <Key> AtIndexes:, countOf<Key> must be implemented. If one of countOf<Key> is found, create a proxy object3;3CountOf <Key>, Enumeratorf<Key>, memberOf<Key> :3All methods must exist, otherwise the step is entered4;4When the AccessInstanceVariables method returns YES (the default is YES), search for instance variables named _<key>, _is< key>, <key>, or is< key> in sequence. If found, simply get the value of the instance variable and continue with the step5. Otherwise, proceed with the procedure6.5If the retrieved property value is an object pointer, simply return the result. If the value is a scalar type supported by NSNumber, store it in an NSNumber instance and return the value. If the result is a scalar type not supported by NSNumber, convert it to an NSValue object and return it.6If none is found, call valueForUndefinedKey: and throw an exception.Copy the code

Using this principle, we add an NSObject+SSJKVC class to NSObject:

// NSObject+SSJKVC.h

@interface NSObject (SSJKVC)
- (void)ssj_setValue:(nullable id)value forKey:(NSString *)key;
@end
Copy the code

NSObject+ ssjkvc. m implementation part, we can get the following pseudo-code according to the previous principle:

// NSObject+SSJKVC.m

#import "NSObject+SSJKVC.h"
#import <objc/runtime.h>
@implementation NSObject (SSJKVC)
#pragma mark -- Setter
- (void)ssj_setValue:(nullable id)value forKey:(NSString *)key{
    /*** -> key (); /*** -> key (); If no, go to Step 2. Step 2 Make sure AccessInstanceVariables returns YES, then look for '_
      
       ', '_is< key>', '
       
        ', or 'is< key>' from the list of instance variables, and set the variables with the input value when found. If no, go to Step 3. (if '_
        
         ' is found, then '_is< key>' is not needed). Step 3. Throw an exception. * /
        
       
      
}

#pragma mark -- getter
- (nullable id)valueForKey:(NSString *)key{
    /*** -> key; /*** -> key; /*** -> key; If no, go to Step 2. CountOf 
      
        and objectIn
       
        AtIndex and 
        
         AtIndexes:, countOf
         
           must be implemented. If countOf
          
            is found, create a proxy object. If no, go to Step 3. Step 3. Determine whether you can assign a value to the instance variable. If you cannot throw an exception, go to Step 4 if you can. Step 4 Sequentially search for instance variables named _
           
            , _is< key>, 
            
             , or is< key>. Retrieves the value of the corresponding instance variable and returns, otherwise returns an empty string */
            
           
          
         
        
       
      
}
Copy the code

ssj_setValue

- (void)ssj_setValue:(nullable id)value forKey:(NSString *)key{
    /*** -> key (); /*** -> key (); If no, go to Step 2. Step 2 Make sure AccessInstanceVariables returns YES, then look for '_
      
       ', '_is< key>', '
       
        ', or 'is< key>' from the list of instance variables, and set the variables with the input value when found. If no, go to Step 3. (if '_
        
         ' is found, then '_is< key>' is not needed). Step 3. Throw an exception. * /
        
       
      
    NSLog(@"Welcome to NSObject+SSJKVC");
    if (key) {
        NSString *bigDealWithKey = [self smallToBigWithFirstCharFrom:key];// Uppercase
        / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * step 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        NSString *selNameOne = [NSString stringWithFormat:@"set%@:",bigDealWithKey];
        NSString *selNameTwo = [NSString stringWithFormat:@"_set%@:",bigDealWithKey];
        BOOL realizationSelOne= [self respondsToSelector:NSSelectorFromString(selNameOne)];
        BOOL realizationSelTwo= [self respondsToSelector:NSSelectorFromString(selNameTwo)];
        
        /// remove the warning from calling performSelector
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        
        / / / search ` set < Key > : `
        if (realizationSelOne) {
            [self performSelector:NSSelectorFromString(selNameOne) withObject:value];
            // find this method, the rest need not be executed
            return;
        }
        / / / search ` _set < Key > `
        if (realizationSelTwo) {
            [self performSelector:NSSelectorFromString(selNameTwo) withObject:value];
            // find this method, the rest need not be executed
            return;
        }
        
        / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        Make sure AccessInstanceVariables returns YES
        if ([self.class accessInstanceVariablesDirectly]) {
            // Find '_
      
       ', '_is< key>', '
       
        ' or 'is< key>' in sequence
       
      
            NSString *_key = [@"_" stringByAppendingString:key];
            NSString *_isKey = [@"_is" stringByAppendingString:bigDealWithKey];
            NSString *isKey = [@"is" stringByAppendingString:bigDealWithKey];
            
            NSMutableArray *ar = [self getIvarListName];// Get all the instance variables
            if ([ar containsObject:_key]) {//_key matches ar
                [self ssj_changeIvar:_key value:value]; // Assign to the instance variable
                return;
            }
            if ([ar containsObject:_isKey]) {
                [self ssj_changeIvar:_isKey value:value];
                return;
            }
            if ([ar containsObject:key]) {
                [self ssj_changeIvar:key value:value];
                return;
            }
            if ([ar containsObject:isKey]) {
                [self ssj_changeIvar:isKey value:value];
                return; }}}/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * step 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
    /// Throw an exception
    [self throwSetterException];
}
Copy the code

Rewrite valueForKey

- (nullable id)valueForKey:(NSString *)key{
    /*** -> key; /*** -> key; /*** -> key; If no, go to Step 2. CountOf 
      
        and objectIn
       
        AtIndex and 
        
         AtIndexes:, countOf
         
           must be implemented. If countOf
          
            is found, create a proxy object. If no, go to Step 3. Step 3. Determine whether you can assign a value to the instance variable. If you cannot throw an exception, go to Step 4 if you can. Step 4 Sequentially search for instance variables named _
           
            , _is< key>, 
            
             , or is< key>. Retrieves the value of the corresponding instance variable and returns, otherwise returns an empty string */
            
           
          
         
        
       
      
    if (key) {
        NSString *bigDealWithKey = [self smallToBigWithFirstCharFrom:key];// Uppercase
        
        / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * step 1 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        NSString *getKey = [NSString stringWithFormat:@"get%@",bigDealWithKey];
        NSString *_key = [NSString stringWithFormat:@"_ % @",key];
        BOOL realization_getKey= [self respondsToSelector:NSSelectorFromString(getKey)];
        BOOL realization_key= [self respondsToSelector:NSSelectorFromString(key)];
        BOOL realization__key= [self respondsToSelector:NSSelectorFromString(_key)];
        
        /// remove the warning from calling performSelector
            #pragma clang diagnostic push
            #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        
        / / / search ` set < Key > : `
        if (realization_getKey) {
            return [self performSelector:NSSelectorFromString(getKey) withObject:key];
        }
        if (realization_key) {
            return [self performSelector:NSSelectorFromString(key) withObject:key];
        }
        if (realization__key) {
            return [self performSelector:NSSelectorFromString(_key) withObject:key];
        }
        
        / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 2 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        CountOf 
      
        and objectIn
       
        AtIndex: and 
        
          AtIndexes:
        
       
      
        NSString *countOfKey = [NSString stringWithFormat:@"countOf%@",bigDealWithKey];
        NSString *objectInKeyAtIndex = [NSString stringWithFormat:@"objectIn%@AtIndex:",bigDealWithKey];
        NSString *keyAtIndexes = [NSString stringWithFormat:@"%@AtIndexes:",key];
        BOOL realization_countOfkey = [self respondsToSelector:NSSelectorFromString(countOfKey)];
        BOOL realization_objectInKeyAtIndex = [self respondsToSelector:NSSelectorFromString(objectInKeyAtIndex)];
        BOOL realization_keyAtIndexes = [self respondsToSelector:NSSelectorFromString(keyAtIndexes)];
        ObjectIn 
      
       AtIndex: and 
       
         AtIndexes: choose one
       
      
        if (realization_countOfkey) {
            if (realization_objectInKeyAtIndex) {
                return [self performSelector:NSSelectorFromString(objectInKeyAtIndex)];
            }else{
                if (realization_keyAtIndexes) {
                    return[self performSelector:NSSelectorFromString(keyAtIndexes)]; }}}/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * step 3 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        if(! [self.class accessInstanceVariablesDirectly] ) { @throw [NSException exceptionWithName:@"LGUnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.****",self] userInfo:nil];
        }
        
        / / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * step 4 * * * * * * * * * * * * * * * * * * * * * * * * * * * *
        /// _
      
       , _is< key>, 
       
        , or is< key> instance variable.
       
      
        NSMutableArray *ar = [self getIvarListName];// Get all the instance variables
        NSString *isKey = [NSString stringWithFormat:@"is%@",bigDealWithKey];
        NSString *_isKey = [NSString stringWithFormat:@"_is%@",bigDealWithKey];
        
        if ([ar containsObject:_key]) {//_key matches ar
            NSLog(@"ssj_IvarValue---%@",[self ssj_IvarValue:_key]);
            return [self ssj_IvarValue:_key]; // Assign to the instance variable
        }else if ([ar containsObject:_isKey]) {
            NSLog(@"ssj_IvarValue---%@",[self ssj_IvarValue:_isKey]);
            return [self ssj_IvarValue:_isKey];
        }else if ([ar containsObject:key]) {
            NSLog(@"ssj_IvarValue---%@",[self ssj_IvarValue:key]);
            return [self ssj_IvarValue:key];
        }else if ([ar containsObject:isKey]) {
            NSLog(@"ssj_IvarValue---%@",[self ssj_IvarValue:isKey]);
            return[self ssj_IvarValue:isKey]; }}return @"";
}
Copy the code

Other public methods

- (void)throwSetterException{
    NSLog(@"Setter throws an exception");
    @throw [NSException exceptionWithName:@"SSJ_UnknownKeyException" reason:[NSString stringWithFormat:@"****[%@ %@]: this class is not key value coding-compliant for the key name.****",self,NSStringFromSelector(_cmd)] userInfo:nil];
}

/// Capitalizes the first character of the string argument
/// @param string Specifies the character to be processed
/// return returns the processed string
- (NSString *)smallToBigWithFirstCharFrom:(NSString *)string{
    if (string) {
        if (string.length == 1) {// If length is 1
            return string.uppercaseString;
        }else if (string.length > 1) {
            // Get the first converted to uppercase, concatenate the rest, and return
            NSString *firstChar = [string substringToIndex:1];
            NSString *atferDeal = firstChar.uppercaseString;
            NSString *endStr = [string substringFromIndex:1];
            return [NSString stringWithFormat:@"% @ % @",atferDeal,endStr];
        }else{
            return @""; }}return nil;
}

/// Assign a value to the instance variable
- (void)ssj_changeIvar:(NSString *)key value:(NSString *)value{
    // Get the corresponding IVAR
   Ivar ivar = class_getInstanceVariable([self class].key.UTF8String);
    // Set the corresponding ivAR value
   object_setIvar(self , ivar, value);
}

/// get the value of the instance variable
- (id)ssj_IvarValue:(NSString *)key{
    // Get the corresponding IVAR
   Ivar ivar = class_getInstanceVariable([self class].key.UTF8String);
    // Set the corresponding ivAR value
    return object_getIvar(self, ivar);
}

/// get all instance variables, return array
- (NSMutableArray *)getIvarListName{
    
    NSMutableArray *mArray = [NSMutableArray arrayWithCapacity:1];
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i<count; i++) {
        Ivar ivar = ivars[i];
        const char *ivarNameChar = ivar_getName(ivar);
        NSString *ivarName = [NSString stringWithUTF8String:ivarNameChar];
// NSLog(@"ivarName == %@",ivarName);
        [mArray addObject:ivarName];
    }
    free(ivars);
    return mArray;
}
Copy the code

test

// SSJPerson
@interface SSJPerson : NSObject{
    @public
    NSString *boddy;
    NSString *_boddy;
    NSString *isBoddy;
    NSString *_isBoddy;
}
@end

@implementation SSJPerson
@end
Copy the code

Run:

code

Baidu network backup link: pan.baidu.com/s/1W0XleXhK… Ur password: 82

nonsense

Every exploration is a growth. Only through continuous learning can we become better and better. Feel free to leave a comment in the comments section