关于setter/getter和点语法的一点迷思 (1)

151 阅读2分钟

故事是这样的, 小顾在看RN的时候, 看见这么一句话:

不要直接给组件 state 赋值(比如this.state.hunger = false)来修改状态。使用 this.setState() 方法才能让 React 知悉状态的变化,从而触发重渲染。直接修改状态变量可能会使界面无法响应!

—— 《React中文网 React基础

因此, 小顾想到了: Objective-C的点语法实际上对应的是getter或setter. 那么如果有一个连续赋值, 是否会引起getter和setter的连续调用, 从而引起(潜在的)调用顺序与预期不一致的问题呢?

小顾于是写了这样的代码做验证:

//  MyClass.h

#import <Foundation/Foundation.h>

@interface MyClass : NSObject

@property (nonatomic, copy) NSString *str; 

@end
//  MyClass.m

#import "MyClass.h"

@implementation MyClass

@synthesize str = _str;

- (NSString *)str {
    NSLog(@"getter called");
    return _str; 
}

- (void)setStr:(NSString *)str {
    NSLog(@"setter called");
    _str = str;
}
@end
//  main.m

#import <Foundation/Foundation.h>

#import "MyClass.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MyClass *c = [[MyClass alloc] init];
        NSString *s = c.str = @"Hello?";
        NSLog(@"s = %@", s);
    }
    return 0;
}

其结果为:

setter called
s = Hello?

即, 只调用了setter, 而未调用getter.

小顾思考了一下才想明白其中的关窍: NSString *s = c.str = @"Hello?";中使用了连续赋值. 赋值运算符(=)是右结合的. 结合性指同一优先级的运算符在表达式中操作的组织方向, 即: 当一个运算对象两侧运算符的优先级别相同时, 运算对象与运算符的结合顺序.

也就是说, 在NSString *s = c.str = @"Hello?";中首先会进行的是c.str = @"Hello?", 这里调用了setter. 然后, 这个语句会被视为: NSString *s = ???, 其中???代表c.str = @"Hello?"的返回值: 赋值运算符=作为一个运算符是有返回值的, 其值为左侧的值. 因此, 此处并没有通过点语法c.str调用到getter, 而是返回了c.str = @"Hello?"的值.

因此, 实际上不存在之前提到的连续赋值对getter/setter调用的影响.