4

In Objective-C:

@interface User : NSObject
@property (nonatomic, strong) NSString  *name;

- (void)setName:(NString *newName) {
    _name = newName
    NSLog("newName = %@", newName);
}

User *user = [[User alloc] init];
user.name = @"Test"; // Call setName method and log "newName = Test"

But in an internal method of the User class:

_name = "Test"; // Don't call setName

In Swift:

class User : NSObject
var name: String {
    didSet {
        print("newName = " + name)
    }
}

How to set name in the internal method of the User class without triggering the didSet observer? Like_name = @"Test" in Objective-C?

Pang
  • 9,564
  • 146
  • 81
  • 122
ZhaoWei
  • 101
  • 1
  • 8

2 Answers2

7

You cannot prevent the didSet from being called. However, if you want to, for some reason, recreate the mechanics of calling the instance-variable directly in Objective-C, avoiding the setter, you can with a computed/stored property pair.

For example:

class User {
    private var _name: String = ""

    var name: String {
        get {
            return _name
        }
        set {
            // any willSet logic
            _name = newValue
            // any didSet logic
        }
    }
}

In essence, this is actually approximately exactly what you actually get in Objective-C when you create a property.

nhgrif
  • 61,578
  • 25
  • 134
  • 173
0

From the Swift docs:

A Swift property does not have a corresponding instance variable, and the backing store for a property is not accessed directly. This approach avoids confusion about how the value is accessed in different contexts and simplifies the property’s declaration into a single, definitive statement.

And:

The willSet and didSet observers for totalSteps are called whenever the property is assigned a new value. This is true even if the new value is the same as the current value.

There's no exact analogy to backing variables integrated into Swift. You can replicate Objective-C properties (see nhgrif's answer). That approach would be foreign in Swift, though, and might be difficult for future programmers to understand. If you don't want to do that, you could either move your didSet code to a separate function…

class User : NSObject {
    var name: String = ""

    func setNameAndPrint(name newName : String) {
        name = newName
        print("newName = " + name)

    }
}

let user = User()

user.name = "Aaron" // doesn't print anything
user.setNameAndPrint(name: "Zhao Wei") // prints "newName = Zhao Wei"

… or you could write more explicit code to mimic this behavior …

class User : NSObject {
    var printNameAfterSetting = true

    var name: String = "" {
        didSet {
            if printNameAfterSetting {
                print("newName = " + name)
            } else {
                printNameAfterSetting = true
            }
        }
    }
}

let user = User()

user.printNameAfterSetting = false
user.name = "Aaron" // prints nothing
user.name = "Zhao Wei" //prints "newName = Zhao Wei"

This example uses a bool, but you could use an enum or other type to represent more complex logic depending on your use case.

Aaron Brager
  • 65,323
  • 19
  • 161
  • 287
  • `"There's no exact analogy."` I'm inclined to disagree. The example from my answer *is* an exact analogy of what you get in Objective-C when you declare a `@property`. It just takes more code in Swift. But it turns out, in most use cases, a `willSet` or `didSet` is actually all that's needed. – nhgrif Oct 24 '15 at 03:11
  • You're right; I'll update. – Aaron Brager Oct 24 '15 at 03:13