Swift: Can't shadow self in closure parameters
| Originator: | brent | ||
| Number: | rdar://18550356 | Date Originated: | 05-Oct-2014 01:23 PM |
| Status: | Open | Resolved: | |
| Product: | Developer Tools | Product Version: | Xcode-Beta (6A1042b) |
| Classification: | Other Bug | Reproducible: | Always |
Summary:
While Swift silently permits you to shadow most variables and constants, `self` is an exception. This causes difficulties in writing some retain-cycle-safe constructs, and should be changed.
Steps to Reproduce:
1. In a playground, try to write a User class which can update its profile image, but ensures that updating the image doesn’t retain the instance:
import Foundation
class User {
var profileImage: NSData?
func updateProfileImage() {
downloadProfileImageForUser(self) { user, image in
self.profileImage = image
}
}
}
func downloadProfileImageForUser(user: User, completion: (User, NSData?) -> Void) {
// Imagine we're downloading asynchronously here.
let image: NSData? = nil
dispatch_async(dispatch_get_main_queue()) { [weak user] in
if let user = user {
completion(user, image)
}
}
}
2. Notice a bug: the completion block in updateProfileImage() is using “self”, not “user”, so it’s holding a strong reference to “self”.
3. Try to modify it defensively so that the outer `self` is shadowed, thus ensuring this mistake is impossible to make again:
downloadProfileImageForUser(self) { self, image in
Expected Results:
This modification compiles and executes.
Actual Results:
An error (specifically, “Expected expression”). Basically, it seems that Swift doesn’t want to let you shadow `self` in this way.
Notes:
What I’m actually trying to do is write a safe block-based NSNotification observer API which implicitly treats the observer—which should usually be the caller’s `self`—as `unowned`. As you probably know, this is a common source of retain cycles. The usual way to design an API to avoid retain cycles is to have it pass the cycle-prone object as a parameter to the block, and then have the API internally manage the reference in the desired way. My extension does this with the addObserver() APIs:< https://gist.github.com/brentdax/64845dc0b3fec0a27d87> The code itself works, but callers will frequently want to shadow `self` in their observer blocks, which isn’t allowed. This makes the code less useful.
Ideally, shadowing `self` in a block should allow you to implicitly call properties and methods on it without warnings. I’d settle for just having the same “must use dot syntax in a block” rules, though.
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!