TL;DR 正确的做法是访问 Objective-C
@property (...) foo
作为 foo
而不是 self.foo
?
详情:
我的一个 iOS 项目仍然使用我几年前编写的一些 Objective-C 代码。
// SomeObject.h
@interface SomeObject : NSObject
@property (nonatomic, readonly) NSString *foo;
@end
// SomeObject.m
@implementation SomeObject { ... }
@synthesize foo;
- (void)someMethod {
foo = @"bar";
}
- (void)otherMethod {
NSLog(@"Foo: %@", foo); // ---> "bar"
}
@end
这按预期工作得很好。现在我想使用 getter 来访问一些新的 Swift 代码,而不是手动设置
foo
:
// SomeObject.m
@implementation SomeObject { ... }
@synthesize foo;
- (NSString *)foo {
return [SwiftObject getFoo];
}
- (void)otherMethod {
NSLog(@"Foo: %@", foo); // ---> nil
NSLog(@"Foo: %@", self.foo); // ---> "swiftBar"
}
@end
因此,仅当使用
self.foo
而不是仅使用 foo
时,使用 getter 才可以正确使用。这不是一个大问题,但需要将现有代码中所有使用的 foo
替换为 self.foo
。
有没有一种干净的方法来避免这种情况,使用
foo
getter 并仍然使用 foo
而不是 self.foo
?
TL;DR 正确的做法是访问 Objective-C
作为@property (...) foo
而不是foo
?self.foo
在没有目标的情况下,不可能使用裸名称访问 Objective-C 属性(即没有
self.
)——这是因为 @property
是在类型上生成 getter + setter methods 的简写,并且方法调用总是需要调用一个对象。
相反,您的第一个代码片段出于不相关的原因而工作:
@property
而没有相应的 @synthesize
指令将会为您生成一个实例变量来存储属性中的实际值。对于像 foo
这样的属性,默认生成的 ivar 将是 _foo
@synthesize
指令为变量提供您自己的名称,并且当您这样做时,您可以使用您想要的任何名称访问 ivar:
@synthesize
指令相当于 @synthesize foo=_foo;
(即将 foo
属性映射到 _foo
ivar)@synthesize foo;
本身相当于 @synthesize foo=foo;
(即将 foo
属性映射到 foo
ivar)@synthesize foo=bar;
将 foo
属性映射到 bar
ivar恰好在 Objective-C 类中,ivars 可以直接通过它们的裸名访问 - 在你的 SomeObject
类中,写
foo
指的是 ivar 本身,not 指的是属性。
foo
属性实际上使用
foo
ivar (因为您没有自己实现
foo
方法),所以
foo
和
self.foo
恰好返回相同的值
foo
指令显式合成
@synthesize
ivar,但随后为
foo
getter 提供您自己的实现,这就是您编写
self.foo
时所调用的。因为
foo
ivar 从未设置过,所以默认情况下它会获得
nil
值
self.foo
,一般来说,建议你避免写
@synthesize foo;
,以避免直接访问方法与直接访问 ivar 时可能发生的混淆。