valueForKeyPath 奔溃

2020-09-22  本文已影响0人  BabyNeedCare

昨天产品反馈了一个奔溃事件, 因为是做职业教育的, 刚好奔溃在课程上, 因此这里做处理总结。

请问下面代码会奔溃吗?

    NSArray *dataArrays = 
@[
        @{@"code": @"ab", @"name": @"apple"},        
        @{@"code": @"cd", @"name": @"flame"}    
    ];

    NSLog(@"----%@---%@",[dataArrays valueForKeyPath:@"name123455"], [[dataArrays valueForKeyPath:@"name123455"] class]);

打印结果

2020-09-22 11:29:04.134991+0800 mjfail[7041:137274] ----(
    "<null>",
    "<null>"
)---__NSArrayI

不会奔溃, 只是返回的是<null>类型的数组


那么假设是一个模型对象数组呢?

创建Person类, 只有2个属性, code, name

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (copy, nonatomic) NSString *code;

@property (copy, nonatomic) NSString *name;

@end

NS_ASSUME_NONNULL_END

NSArray *dataArrays = @[
    
    @{@"code": @"ab", @"name": @"apple"},
    
    @{@"code": @"cd", @"name": @"flame"}
    
];


NSArray *data = [Person mj_objectArrayWithKeyValuesArray:dataArrays];

NSLog(@"%@", [data valueForKeyPath:@"点点滴滴"]);

这次会奔溃吗?

2020-09-22 11:31:12.927584+0800 mjfail[7073:139529] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x60000330ad80> valueForUndefinedKey:]: this class is not key value coding-compliant for the key 点点滴滴.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff23c4f02e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff50b97b20 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff23c4ebf9 -[NSException raise] + 9
    3   Foundation                          0x00007fff256f3f57 -[NSObject(NSKeyValueCoding) valueForUndefinedKey:] + 225
    4   Foundation                          0x00007fff256f2bca -[NSObject(NSKeyValueCoding) valueForKey:] + 317
    5   Foundation                          0x00007fff256f4abb -[NSArray(NSKeyValueCoding) valueForKey:] + 411
    6   Foundation                          0x00007fff256f4e6a -[NSArray(NSKeyValueCoding) valueForKeyPath:] + 416
    7   mjfail                              0x0000000100b2bc7b -[ViewController viewDidLoad] + 459
    8   UIKitCore                           0x00007fff471cdb25 -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 83
    9   UIKitCore                           0x00007fff471d2a7e -[UIViewController loadViewIfRequired] + 1084
    10  UIKitCore                           0x00007fff471d2e9b -[UIViewController view] + 27
    11  UIKitCore                           0x00007fff4788742d -[UIWindow addRootViewControllerViewIfPossible] + 150
    12  UIKitCore                           0x00007fff47886ae4 -[UIWindow _updateLayerOrderingAndSetLayerHidden:actionBlock:] + 232
    13  UIKitCore                           0x00007fff47887ba1 -[UIWindow _setHidden:forced:] + 362
    14  UIKitCore                           0x00007fff4789af4d -[UIWindow _mainQueue_makeKeyAndVisible] + 42
    15  UIKitCore                           0x00007fff47aa669d -[UIWindowScene _makeKeyAndVisibleIfNeeded] + 202
    16  UIKitCore                           0x00007fff46ddbe81 +[UIScene _sceneForFBSScene:create:withSession:connectionOptions:] + 1405
    17  UIKitCore                           0x00007fff4784bf1a -[UIApplication _connectUISceneFromFBSScene:transitionContext:] + 1018
    18  UIKitCore                           0x00007fff4784c25c -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 304
    19  UIKitCore                           0x00007fff473b920d -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
    20  FrontBoardServices                  0x00007fff36555225 -[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:] + 442
    21  FrontBoardServices                  0x00007fff3657b598 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.154 + 102
    22  FrontBoardServices                  0x00007fff3655fd05 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 220
    23  FrontBoardServices                  0x00007fff3657b229 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke + 355
    24  libdispatch.dylib                   0x0000000100e74d48 _dispatch_client_callout + 8
    25  libdispatch.dylib                   0x0000000100e77cb9 _dispatch_block_invoke_direct + 300
    26  FrontBoardServices                  0x00007fff365a143e __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
    27  FrontBoardServices                  0x00007fff365a112c -[FBSSerialQueue _queue_performNextIfPossible] + 441
    28  FrontBoardServices                  0x00007fff365a163b -[FBSSerialQueue _performNextFromRunLoopSource] + 22
    29  CoreFoundation                      0x00007fff23bb2221 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    30  CoreFoundation                      0x00007fff23bb214c __CFRunLoopDoSource0 + 76
    31  CoreFoundation                      0x00007fff23bb197c __CFRunLoopDoSources0 + 268
    32  CoreFoundation                      0x00007fff23bac62f __CFRunLoopRun + 1263
    33  CoreFoundation                      0x00007fff23babe16 CFRunLoopRunSpecific + 438
    34  GraphicsServices                    0x00007fff38438bb0 GSEventRunModal + 65
    35  UIKitCore                           0x00007fff4784fb48 UIApplicationMain + 1621
    36  mjfail                              0x0000000100b2c0b4 main + 116
    37  libdyld.dylib                       0x00007fff51a1dc25 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

奔溃很彻底。

专成对象后, 再使用valueForKeyPath, 找不到对应的属性, 就会奔溃。

可是项目中有很多地方使用到了valueForKeyPath, 在每个对应模型中加指定属性, 范围太大, 方法不可取。

那么, 我想到用运行时拦截方法。

  1. 创建NSArray的分类

  2. 在.m文件中实现交换方法


#import "NSArray+updateArrays.h"
#import <objc/runtime.h>
#import "MJExtension.h"
@implementation NSArray (updateArrays)

+ (void)load {
    
    Class class = [self class];
    SEL originalSelector = NSSelectorFromString(@"valueForKeyPath:");
    SEL swizzledSelector = NSSelectorFromString(@"transiValueForKeyPath:");
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

}

- (nullable id)transiValueForKeyPath:(NSString *)keyPath {
    
    __block BOOL isHaveKeyPath = NO;

    [self enumerateObjectsUsingBlock:^(NSObject * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

        if ([obj.mj_keyValues.allKeys containsObject:keyPath]){

            isHaveKeyPath = YES;

            *stop = YES;
        }
    }];

    if (isHaveKeyPath) {

        return [self transiValueForKeyPath:keyPath];

    } else {

        return nil;
    }
}


@end

这样就可以完美解决同类型的问题了!

上一篇 下一篇

猜你喜欢

热点阅读