您好,欢迎来到二三四教育网。
搜索
您的当前位置:首页OC中KVO属性互相影响探究

OC中KVO属性互相影响探究

来源:二三四教育网

keyPathsForValuesAffectingXXXkeyPathsForValuesAffectingValueForKey这两个函数是表示若干个属性在KVO的情况下是否相互影响。
举例来说有a和b两个属性,如果a的变化影响b,我们使用KVO监听b,当我们在更改a的时候,如何让监听的b能收到消息,上述两个方法就是解决这个问题的。
前置说明
RXAFNTest4Object 是包含各种互相影响的属性的类
RXAFNTestDependPropertyObject是测试类
RXAFNTestDependPropertyObject.m文件中:

@interface RXAFNTestDependPropertyObject ()
@property (nonatomic, strong) RXAFNTest4Object *test4Object;
@end

@implementation RXAFNTestDependPropertyObject
// 监听属性变化的
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    NSLog(@"KeyPath:%@, change.new:%@", keyPath, change[@"new"]);
}
@end

一对一的影响:b的值依赖a(a影响b),b是只读的

RXAFNTest4Object.h

@property (nonatomic, assign) int a;
@property (nonatomic, readonly) int b;

RXAFNTest4Object.m

- (int)b
{
    return self.a + 1;
}
+ (NSSet *)keyPathsForValuesAffectingB
{
    return [NSSet setWithObjects:@"a", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_A_B {
    [self.test4Object addObserver:self forKeyPath:@"b" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.a = 5;
    });
}

查看observeValueForKeyPath:ofObject:change:context中的输出:

KeyPath:b, change.new:6

也就是说添加的b的监听,变化a的时候因为keyPathsForValuesAffectingB的存在,b收到这个变化了

一对一的影响:b1的值依赖a1, 注意b1与b的区别

RXAFNTest4Object.h

@property (nonatomic, assign) int a1;
@property (nonatomic, assign) int b1;

RXAFNTest4Object.m

+ (NSSet *)keyPathsForValuesAffectingB1
{
    return [NSSet setWithObjects:@"a1", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_A1_B1 {
    [self.test4Object addObserver:self forKeyPath:@"b1" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.a1 = 5;
    });
}

输出

KeyPath:b1, change.new:0

如果不考虑最终b1的值的话,效果是跟ab一致的。

一对一的影响:d的值依赖c,在c的赋值过程中会重新赋值d

RXAFNTest4Object.h

@property (nonatomic, assign) int c;
@property (nonatomic, assign) int d;

RXAFNTest4Object.m

- (void)setC:(int)c
{
    _c = c;
    self.d = _c + 2;
}
+ (NSSet *)keyPathsForValuesAffectingD
{
    return [NSSet setWithObjects:@"c", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_C_D
{
    [self.test4Object addObserver:self forKeyPath:@"d" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.c = 5;
    });
}

查看输出结果

KeyPath:d, change.new:7
KeyPath:d, change.new:7

d的变化通知会输出两遍,出现这个原因是self.d = _c + 2;keyPathsForValuesAffectingD都会触发KVO,解决这个bug(如果这个算bug的话),让它只调用一次有两种方法:

  • 删除keyPathsForValuesAffectingD方法
  • self.d = _c + 2;改为_d = _c + 2;

多个影响1个

RXAFNTest4Object.h

@property (nonatomic, assign) int c1;
@property (nonatomic, assign) int c2;
@property (nonatomic, assign) int c3;
@property (nonatomic, assign) int d1;

RXAFNTest4Object.m

+ (NSSet *)keyPathsForValuesAffectingD1
{
    return [NSSet setWithObjects:@"c1", @"c2", @"c3", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_C1_C2_C3_D1
{
    [self.test4Object addObserver:self forKeyPath:@"d1" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.c1 = 10;
        self.test4Object.c2 = 20;
        self.test4Object.c3 = 30;
    });
}

输出结果

KeyPath:d1, change.new:0
KeyPath:d1, change.new:0
KeyPath:d1, change.new:0

输出三遍是合理的,因为在dispatch_afterc1c2c3依次变化了

AFN中AFSecurityPolicy中pinnedCertificates与pinnedPublicKeys属性互相通知影响

RXAFNTest4Object.h

// e: pinnedCertificates
// f: pinnedPublicKeys
@property (nonatomic, assign) int e;

RXAFNTest4Object.m

@interface RXAFNTest4Object ()
@property (nonatomic, assign) int f;
@end
@implementation RXAFNTest4Object
- (void)setE:(int)e
{
    _e = e;
    self.f = _e + 3;
}

+ (NSSet *)keyPathsForValuesAffectingF
{
    return [NSSet setWithObjects:@"e", nil];
}
@end

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_E_F
{
    [self.test4Object addObserver:self forKeyPath:@"f" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.e = 5;
    });
}

输出结果

KeyPath:f, change.new:8
KeyPath:f, change.new:8

同样的输出两遍跟cd的效果(bug?)一样。

一对一的影响:另一种实现方法

RXAFNTest4Object.h

@property (nonatomic, assign) int g;
@property (nonatomic, assign) int h;

RXAFNTest4Object.m+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

// 注意这里的区别,g影响h,这里要返回g,并指明是g的变化影响h
    if ([key isEqualToString:@"h"]) {
        return [NSSet setWithObject:@"g"];
    }

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_G_H
{
    [self.test4Object addObserver:self forKeyPath:@"h" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.g = 5;
    });
}

输出结果

KeyPath:h, change.new:0

效果跟a1,b1一致

一个影响多个

RXAFNTest4Object.h

@property (nonatomic, assign) int g1;
@property (nonatomic, assign) int h1;
@property (nonatomic, assign) int h2;
@property (nonatomic, readonly) int h3;

RXAFNTest4Object.m+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

if ([key isEqualToString:@"h1"] || [key isEqualToString:@"h2"] || [key isEqualToString:@"h3"]) {
    return [NSSet setWithObject:@"g1"];
}

RXAFNTest4Object.m

- (int)h3
{
    return self.g1 + 3;
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_G1_H1_H2_H3
{
    [self.test4Object addObserver:self forKeyPath:@"h1" options:NSKeyValueObservingOptionNew context:nil];
    [self.test4Object addObserver:self forKeyPath:@"h2" options:NSKeyValueObservingOptionNew context:nil];
    [self.test4Object addObserver:self forKeyPath:@"h3" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.g1 = 10;
    });
}

输出结果

KeyPath:h3, change.new:13
KeyPath:h2, change.new:0
KeyPath:h1, change.new:0

一影响多,同理也是可用keyPathsForValuesAffectingXXX去实现

这个顺序是怎么定义的我是不清楚的了,这个估计需要看相关的源码吧

互相影响

RXAFNTest4Object.h

@property (nonatomic, assign) int i;
@property (nonatomic, assign) int j;

RXAFNTest4Object.m

+ (NSSet *)keyPathsForValuesAffectingI
{
    return [NSSet setWithObjects:@"j", nil];
}
+ (NSSet *)keyPathsForValuesAffectingJ
{
    return [NSSet setWithObjects:@"i", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_I_J_1
{
    [self.test4Object addObserver:self forKeyPath:@"i" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.j = 10;
    });
}
- (void)test_dependProperty_I_J_2
{
    [self.test4Object addObserver:self forKeyPath:@"j" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.i = 10;
    });
}

test_dependProperty_I_J_1test_dependProperty_I_J_2分别能得到想要的结果

多个影响多个-- 方法一: 使用keyPathsForValuesAffectingXXX

RXAFNTest4Object.h

@property (nonatomic, assign) int l1;
@property (nonatomic, assign) int l2;
@property (nonatomic, assign) int m1;
@property (nonatomic, assign) int m2;

RXAFNTest4Object.m

+ (NSSet *)keyPathsForValuesAffectingM1
{
    return [NSSet setWithObjects:@"l1", @"l2", nil];
}
+ (NSSet *)keyPathsForValuesAffectingM2
{
    return [NSSet setWithObjects:@"l1", @"l2", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_L1_L2_M1_M2
{
    [self.test4Object addObserver:self forKeyPath:@"m1" options:NSKeyValueObservingOptionNew context:nil];
    [self.test4Object addObserver:self forKeyPath:@"m2" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.l1 = 10;
        self.test4Object.l2 = 10;
    });
}

输出结果

KeyPath:m2, change.new:0
KeyPath:m1, change.new:0
KeyPath:m2, change.new:0
KeyPath:m1, change.new:0

输出的结果是合理的

多个影响多个-- 方法二: 使用keyPathsForValuesAffectingValueForKey

RXAFNTest4Object.h

@property (nonatomic, assign) int p1;
@property (nonatomic, assign) int p2;
@property (nonatomic, assign) int q1;
@property (nonatomic, assign) int q2;

RXAFNTest4Object.m+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

if ([key isEqualToString:@"q1"] || [key isEqualToString:@"q2"]) {
    return [NSSet setWithObjects:@"p1", @"p2", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_P1_P2_Q1_Q2
{
    [self.test4Object addObserver:self forKeyPath:@"q1" options:NSKeyValueObservingOptionNew context:nil];
    [self.test4Object addObserver:self forKeyPath:@"q2" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.p1 = 10;
        self.test4Object.p2 = 10;
    });
}

输出结果

KeyPath:q2, change.new:0
KeyPath:q1, change.new:0
KeyPath:q2, change.new:0
KeyPath:q1, change.new:0

l1,l2,m1,m2一致

多个影响多个 keyPathsForValuesAffectingXXX 与 keyPathsForValuesAffectingValueForKey 一起使用

RXAFNTest4Object.h

@property (nonatomic, assign) int r1;
@property (nonatomic, assign) int r2;
@property (nonatomic, assign) int s1;
@property (nonatomic, assign) int s2;

RXAFNTest4Object.m

+ (NSSet *)keyPathsForValuesAffectingS1
{
    return [NSSet setWithObjects:@"r1", @"r2", nil];
}
+ (NSSet *)keyPathsForValuesAffectingS2
{
    return [NSSet setWithObjects:@"r1", @"r2", nil];
}

RXAFNTest4Object.m+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key

if ([key isEqualToString:@"s1"] || [key isEqualToString:@"s2"]) {
    return [NSSet setWithObjects:@"r1", @"r2", nil];
}

RXAFNTestDependPropertyObject.m

- (void)test_dependProperty_R1_R2_S1_S2
{
    [self.test4Object addObserver:self forKeyPath:@"s1" options:NSKeyValueObservingOptionNew context:nil];
    [self.test4Object addObserver:self forKeyPath:@"s2" options:NSKeyValueObservingOptionNew context:nil];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.test4Object.r1 = 10;
        self.test4Object.r2 = 10;
    });
}

输出结果

KeyPath:s2, change.new:0
KeyPath:s1, change.new:0
KeyPath:s2, change.new:0
KeyPath:s1, change.new:0

l1,l2,m1,m2一致

Copyright © 2019- how234.cn 版权所有 赣ICP备2023008801号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务