[英]Why do properties modified by @optional become immutable?
I have an Objective-C protocol that contains a property as follows:我有一个包含如下属性的Objective-C协议:
#import <Foundation/Foundation.h>
@protocol Playback <NSObject>
@optional
@property (nonatomic, nonnull) NSURL *assetURL;
@end
PlayerController
has a property of type id<Playback>
: PlayerController
有一个id<Playback>
类型的属性:
@interface PlayerController: NSObject
@property (nonatomic, strong, nonnull) id<Playback> currentPlayerManager;
@end
I tried to write the following code in Swift , but I got an error :我尝试在Swift中编写以下代码,但出现错误:
var player = PlayerController()
var pla = player.currentPlayerManager
pla.assetURL = URL(string: "123") // ❌ Cannot assign to property: 'pla' is immutable
If I comment out the @optional
for the Playback
protocol, then it compiles fine.如果我注释掉
Playback
协议的@optional
,那么它编译得很好。
This makes me wonder why @optional
would cause this error?这让我想知道为什么
@optional
会导致这个错误?
Since it's optional, Swift can't guarantee the setter is implemented.由于它是可选的,Swift 不能保证 setter 的实现。
From Jordan Rose (who worked on Swift at the time that SE-0070 was implemented) on the forums :来自论坛上的Jordan Rose(在实施SE-0070时从事 Swift 工作):
Normally optional requirements add an extra level of optionality:
通常可选要求会增加额外的可选性:
- Methods become optional themselves (
f.bar?()
)方法本身成为可选的(
f.bar?()
)- Property getters wrap the value in an extra level of
Optional
(if let bar = f.bar
)属性 getter 将值包装在
Optional
的额外级别(if let bar = f.bar
)But there's nowhere to put that extra level of Optional for a property setter.
但是对于属性设置器来说,没有地方可以放置额外的 Optional 级别。 That's really the entire story: we never figured out how to expose optional property setters in a safe way, and didn't want to pick any particular unsafe solution.
这就是整个故事:我们从来没有想出如何以安全的方式公开可选的属性设置器,并且不想选择任何特定的不安全解决方案。 If someone can think of something that'd be great!
如果有人能想到一些很棒的东西!
So the answer appears to be: at the time that optional
protocol requirements were intentionally limited to Objective-C protocols in Swift ( SE-0070 ), no spelling for an explicit implementation of this was decided on, and it appears that this functionality is uncommon enough that this hasn't really come up since.所以答案似乎是:在
optional
协议要求被故意限制为 Swift ( SE-0070 ) 中的 Objective-C 协议时,没有决定明确实现的拼写,而且看起来这个功能并不常见,以至于从那以后就没有真正出现过。
Until (and if) this is supported, there are two potential workarounds:直到(如果)这被支持,有两种潜在的解决方法:
Introduce an explicit method to Playback
which assigns a value to assetURL
向
Playback
引入一个显式方法,该方法为assetURL
-setAssetURL:
because it will be imported into Swift as if it were the property setter instead of a method, and you still won't be able to call it.-setAssetURL:
因为它将被导入 Swift 中,就好像它是属性设置器而不是方法一样,您仍然无法调用它。 (This is still true if you mark assetURL
as readonly
) assetURL
标记为readonly
,这仍然是正确的)extension
because you still can't assign to the protocolextension
中为该方法提供实现,因为您仍然无法分配给协议Do like you would in Swift and introduce a protocol hierarchy , where, for example, an AssetBackedPlayback
protocol inherits from Playback
and offers assetURL
as a non- @optional
-property instead:像在 Swift 中那样做,并引入协议层次结构,例如,
AssetBackedPlayback
协议继承自Playback
并提供assetURL
作为非@optional
:
@protocol Playback <NSObject> // Playback methods @end @protocol AssetBackedPlayback: Playback @property (nonatomic, nonnull) NSURL *assetURL; @end
You would then need to find a way to expose PlayerController.currentPlayerManager
as an AssetBackedPlayback
in order to assign the assetURL
.然后,您需要找到一种方法将
PlayerController.currentPlayerManager
公开为AssetBackedPlayback
以便分配assetURL
。
Some additional alternatives from Jordan:约旦的一些其他替代品:
I think the original recommended workaround was "write a
static inline
function in Objective-C to do it for you", but that's not wonderful either.我认为最初推荐的解决方法是“在 Objective-C 中编写一个
static inline
function 来为你做这件事”,但这也不是很好。setValue(_:forKey:)
can also be good enough in practice if it's not in a hot path.如果
setValue(_:forKey:)
不在热门路径中,它在实践中也足够好。
The static inline
function recommendation can function similarly to a default protocol implementation, but you do need to remember to call that function instead of accessing the property directly. The
static inline
function recommendation can function similarly to a default protocol implementation, but you do need to remember to call that function instead of accessing the property directly.
setValue(_:forKey:)
will also work, but incurs a noticeable performance penalty because it supports a lot of dynamism through the Objective-C runtime, and is significantly more complicated than a simple assignment. setValue(_:forKey:)
也可以工作,但会导致明显的性能损失,因为它通过 Objective-C 运行时支持很多动态,并且比简单的分配要复杂得多。 Depending on your use-case, the cost may be acceptable in order to avoid complexity!根据您的用例,成本可能是可以接受的,以避免复杂性!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.