-1

I have a question that I suppose the answer is no, but I want to be sure. Is there any way to trigger a method when a class's property values were changed from another class? An example, in iOS:

I've got FirstViewController, that get's an NSString from the server and saves it as @property NSString helloString. But it also saves it locally, using NSSUserDefaults, so when the user enters the app he will see the last helloString saved locally and when server answers (let's say after 5 seconds) it updates this helloString property.

Imagine that the user goes to SecondViewController (where we passed helloString with prepareForSegue in FirstViewController and saved as a property in SecondViewController). SecondViewController displays the saved helloString, and after 5 seconds server answers and updates the helloString property in FirstViewController (and of course to the SecondViewController).

Is there any way (I thought I could use NSNotificationCenter) to trigger a method in SecondServerController when helloString value changed in FirstServerController?

EDIT:

KVO is not a valid option because if we had a ThirdViewController and user went from FirstViewController to SecondViewController and finally to ThirdViewController, and then go back to FirstViewController going through SecondViewcontroller, on [FirstViewController removerObserver:forKeyPath:] method that is inside SecondViewController would crash because FirstViewController was released from memory.

Thank you for your time.

aramusss
  • 2,391
  • 20
  • 30
  • You'll probably find that this http://stackoverflow.com/questions/4282493/what-is-key-value-coding-and-key-value-observing-in-objective-c will help you. The answers give a good example of KVC (Key-Value Coding) and KVO (Key-Value Observing). – Popeye Mar 09 '15 at 10:55
  • possible duplicate of [Understanding KVO in iOS](http://stackoverflow.com/questions/4813331/understanding-kvo-in-ios) – Basheer_CAD Mar 09 '15 at 11:03
  • Yet both questions lead to similar answer, I believe the question is different, this one asks about a pratical case while yours is just asking for a better theorical explanation about KVO. – aramusss Mar 09 '15 at 14:04

3 Answers3

3

Actually, the answer is yes, you can do that with Key-Value observing.

In the prepareForSegue you can add the observer:

[self addObserver:destinationViewController forKeyPath:@"helloString" options:<#(NSKeyValueObservingOptions)#> context:NULL];

Now, when helloString change, the method observeValueForKeyPath:ofObject:change:context: will be called in SecondViewController.

Lord Zsolt
  • 6,492
  • 9
  • 46
  • 76
  • That would be the solution if it had only 2 ViewControllers but it can cause crashes: If we have a `ThirdViewController` and user goes through First-> Second -> Third, the `FirstViewController` deallocates from memory and if the user goes back to `FirstViewController` it makes crash on `removeObserver`. I will post a solution using `[NSNotificationCenter defaultCenter]` – aramusss Mar 10 '15 at 11:37
  • Well obviously you remove the observer on the counterpart of the method it was added on. Added on `viewDidLoad`, remove on `dealloc`, viewDidAppear - viewDidDisappear, etc. – Lord Zsolt Mar 10 '15 at 12:04
2

In your case, KVO will be overkill. You can simply override the property's setter method.

For example, in the SecondViewController.h, you have the property:

@property (strong, nonatomic) NSString *helloString;

In your SecondViewController.m file, add the method:

-(void)setHelloString:(NSString*)helloString {

    //This method gets called when the helloString property is assigned a value.
    //Save the value in the iVar
    _helloString = helloString;

    //Take whatever other action that is necessary.
}
ZeMoon
  • 20,054
  • 5
  • 57
  • 98
  • 1
    I'm pretty sure `nonatimic` has nothing to do with setter/getter generation. Getter/setter is generated due to `@property`, `nonatomic` just defines what type of setter/getter it generates. – Lord Zsolt Mar 09 '15 at 10:55
  • As it says in the Apple Docs: `You can use the nonatomic property attribute to specify that synthesized accessors simply set or return a value directly, with no guarantees about what happens if that same value is accessed simultaneously from different threads`. – ZeMoon Mar 09 '15 at 10:59
  • So if i have two setters for `helloString` in first and second viewcontrollers, and i modify `helloString` anywhere, both setters will be called? – aramusss Mar 09 '15 at 10:59
  • The setter will be only for the class for which it has been overridden. You can create a cascade effect, by setting the secondViewController's property from the setter of the firstViewController – ZeMoon Mar 09 '15 at 11:00
  • The custom setter should be in FirstViewController.m in this case (due to the question). `nonatomic` and `atomic` simply define the thread-safety of the setter and getter methods – croX Mar 09 '15 at 11:06
  • I did thought about this solution, but I think @LordZsolt is cleaner. Thanks for your time! – aramusss Mar 09 '15 at 11:08
  • ZeeMoon and @LordZsolt, Lord Zsolt is right. The nonatomic qualifier says that you won't be accessing the same property from different threads. The opposite of nonoatomic is atomic (which is the default). When a property is atomic the system adds lock commands to the getter and setter to avoid multiple threads accessing the property at the same time, but as Lord Zsolt says, it doesn't determine whether or not the getter/setter is generated. – Duncan C Mar 09 '15 at 11:09
  • It used to be that you had to add an @synthesize directive in order to get the system to create your getters and setters. Now it's automatic. `@dynamic` tells the system NOT to generate your getters/setters and trust you that they will be provided at runtime. – Duncan C Mar 09 '15 at 11:14
0

The answer was not KVO or custom setter, for my case. I finally used [NSNotificationCenter defaultCenter] and post a notification from FirstViewController that will be observed in SecondViewController, and if any of those are released will not make the app crash.

aramusss
  • 2,391
  • 20
  • 30