So I have a fetched result controller that returns a list of objects.
I am trying to detect any changes to the object including changes to sub objects
Object has these properties (NSString myString, NSSNumber myNumber, Object2 myObject2)
so any changes to myString, and myNumber would cause the FetchedResultController to notify the delegate. But changes to myObject2 properties won't trigger the fetchedResultController to call the delegate.
Am i doing anything wrong? Am i suppose to manually let myObject know that it has to notify the fetchedResultController?
When you "change" myString and your NSFetchedResultsController (NSFRC) responds with its event, you need to realize what is actually occurring.
The NSFRC is using Key Value Observing to see your change. Now, assuming yourObj is your object that you mentioned above, and so when you type: yourObj.myString = #"New String" or [yourObj setMyString:#"New String"] any receivers observing that property (your NSFRC!) will be notified, because the setter is being called on your object.
Therefore, you are not receiving notifications on your "sub object" as you called it, because you are not accessing a property that is being observed by the NSFRC, rather a property of your sub object.
To trigger a notification of your sub object, you would need to call the sub object setter on your object. (ie. [myObj setObject2:obj2])
So here is the answer:
implement the following methods in child class (myObject2), and on every keyValue changes notify the owner class
- (void)willChangeValueForKey:(NSString *)key
{
[super willChangeValueForKey:key];
[self.myMainObject willChangeValueForKey:#"myObject2"];
}
- (void)didChangeValueForKey:(NSString *)key
{
[super didChangeValueForKey:key];
[self.myMainObject didChangeValueForKey:#"myObject2"];
}
Related
I have an object that is set as the delegate of another object, whose delegate property is weak.
- (YYService *)service
{
XXHandler *handler = [[XXHandler alloc] init];
// YYService's "delegate" property is weak
return [[YYService alloc] initWithDelegate:handler];
// The XXHandler is deallocated because there are no strong references to it
}
Since nothing else references the delegate it ends up getting deallocated, but I want it to live for as long as the parent object does as if the parent had a strong reference to its delegate. Is there a simple way to accomplish this?
The easy why to "solve" that problem is to subclass YYService, giving the subclass an additional strong property and set that one in -initWithDelegate:.
But this "solution" would deepen a problem in your design instead of solving that.
Let's have a look, why delegates are usually hold weakly:
The delegating class has a general – or no – behavior which might not fit in the class' user's case, i. e. if something happens. (An operation completes, an error occurs, $whatever) So the delegating class gives you the opportunity to customize the behavior including running custom code. Delegating is in competition with subclassing, but in difference to subclassing is on a per instance basis (instead of a per class basis) and at run time (instead of compile time).
Because it works on per instance basis, the instance creating the delegate typically holds the delegating instance strongly. This code knows the customization that should apply to the delegating instance:
-(void)createDelegate
{
self.delegating = [Delegating new]; // I create and hold the instance strongly
delegating.delegate = self; // I customize it
}
Then the delegating instance cannot hold the delegate strongly, because this would be a retain cycle.
In your snippet that does not work, because -service returns the newly created delegating instance. And even it would be possible to return both instances, I wouldn't like it, because creating the delegating object and installing the delegate would be a two-step operation, even it is semantically a one-stepper. So If you do not have self as the delegate, you should do the whole installation process in one method:
-(void)installService
{
self.handler = [[XXHandler alloc] init]; // Hold the handler strongly
self.service = [[YYService alloc] initWithDelegate:handler];
}
If you do not know the concrete instance object acting as delegate, pass it as argument:
-(void)installServiceWithDelegate:(id)delegate
{
self.delegate = delegate;
self.service = [[YYService alloc] initWithDelegate:delegate];
}
…
[self installServiceWithDelegate:[YourConcreteClass new]];
But you should not try to turn things upside down or inside out.
I'm new to Objective-C and I got really confused when I saw the if check in the getter of a property:
- (XXX)name {
if (!_name) _name = [[XXX alloc] init];
return _name;
}
Why do you have to check if the pointer is nil when instantiating? Isn't that all objects starts with 0(nil)? Why can't you just have the pointer point to the newly instantiated object on the left?
You can see the point of this when you consider that name is called several times. The first call on a particular instance will allocate _name. In the subsequent calls _name wouldn't be nil, so the previously allocated item would be returned.
This is a lazy initialization pattern. This implementation is fine in single-threaded environments, and in environments where objects with this method are not shared among threads.
In concurrent environments you should use a thread-safe version of this pattern, which uses a lock, or the dispatch_once method.
Why do you have to check if the pointer is nil when instantiating?
The second time you call the getter method, it's already instantiated. This pattern is used when you only want to instantiate the property once. If it's nil you haven't done it yet. If it's non nil just return the value.
Isn't that all objects starts with 0(nil)?
Yep. If it's nil that means you need to instantiate it. So go ahead and do that, and from then on return that instance.
Why can't you just have the pointer point to the newly instantiated object on the left?
Huh? I have no idea what you are asking here.
This is a very common mini-pattern in Objective-C. You see it, for example, in custom property getters. The idea is to create an object, but only if you haven't created one before (and if you have, just return it). As #Nicholas Hart says in his comment, this also helps achieves lazy initialization (an object is created if and when it is referenced.
E.g.:
- (MyType *)myProperty
{
if(!_myProperty)
{
_myProperty = [[MyType alloc] init];
}
return _myProperty;
}
// somewhere else, you want to use the property:
[self.myProperty doSomething];
In the call to doSomething, the getter method myProperty will be called, and the _myProperty ivar (which is behind the myProperty property) will be initialized, if necessary.
Here are 2 lines of code from Apple's own SimpleStocks sample code (APLSimpleStockView.m)
NSInteger dataCount = [self.dataSource graphViewDailyTradeInfoCount:self];
NSArray *sortedMonths = [self.dataSource graphViewSortedMonths:self];
The first code line above looks like "dataSource" is the recipient of message graphViewDailyTradeInfoCount:self (which returns an NSInteger).
The second line of code above looks like "dataSource" is now the recipient of message graphViewSortedMonths:self (which returns an NSArray *).
The only reference to dataSource I can find (in APLSimpleStockView.h) has it being a property, not an object/class instance? How come I can send a property a message? I thought I can only get and set a property's value?
The end result of the code is that after line 1, dataCount contains a number, and after line 2, sortedMonths contains an array of sorted month names. But where does this behaviour come from, since I cant find any place in the sample where dataSource causes anything to be returned when sent a message.
Is self.dataSource acting as both a getter and a setter here?
I thought I can only get and set a property's value?
That's correct, but what's the value of a property? In this case it's an object, and you can definitely send a message to an object.
The code is equivalent to using the getter for the property, assigning the result to a variable, and then sending the message:
WhateverClassTheDataSourceIs * dS = self.dataSource;
NSInteger dataCount = [dS graphViewDailyTradeInfoCount:self];
The additional assignment just isn't necessary.
(Your code could also be written
[[self dataSource] graphViewDailyTradeInfoCount:self];
If that makes it clearer for you.)
How come I can send a property a message?
A property is just a promise to provide accessor methods for a given name. If the property is foo, the accessors are typically -foo and -setFoo:. So, in this case, self.dataSource returns an object that receives the message.
If you look at the APLSimpleStockView interface, you'll see the property declared as a pointer to an object:
#property (nonatomic, weak) IBOutlet id<APLSimpleStockViewDataSource> dataSource;
That means that dataSource is an id (that is, a pointer to an object) that implements the APLSimpleStockViewDataSource protocol. Also, it's marked as an outlet so that you can set it in Interface Builder. Accordingly, self.dataSource returns an id (again, an object pointer) that refers to the view's data source.
Is "self.dataSource acting as both a getter and a setter here?
No, it's just a getter returning the data source object.
Is this simple? I'm actually trying to monitor if an object changes (to determine if I should save it). Currently I just have an array in the object with a list of all of it's readwrite properties, then I loop through it after the object is created and add observers:
for ( NSString *observer in _observers ){
[self addObserver: self forKeyPath: observer options: NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context: nil];
}
It works, but if you forget to add a property to the array, obviously the observer won't be called. Does anyone know how I can just determine the object's properties at runtime? I was thinking it may be around respondsToSelector: but I haven't been able to find much on the subject.
Thanks in advance!
Properties of an object, after they have been synthesized, behave almost like ordinary object's methods, so you can do following check
if ([myObject respondsToSelector: #selector(propertyName)]) {
// your code here
}
Or if you want to use strings as selector's name:
if ([myObject respondsToSelector: NSSelectorFromString(#"propertyName")]) {
// your code here
}
Here propertyName is a getter (it's signature name exactly corresponds to your declared property name), so if you want to check for setter presence, you should add additional expression:
[myObject respondsToSelector: #selector(setPropertyName:)])
May be this will help:
You can get list of properties in a class using class_copyPropertyList
objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount)
and then from each property you can get its name using property_getName function and attributes using property_getAttributes function (if you need to filter read-write properties).
For more details see Objective-c Runtime Reference
Can someone explain in simple terms what is Key-Value-Coding and Key-Value-Observing? Please don't provide links to Apple Developer's reference Document. I have gone through them. I expect an explanation in very simple terms.
Key-Value-Coding (KVC) means accessing a property or value using a string.
id someValue = [myObject valueForKeyPath:#"foo.bar.baz"];
Which could be the same as:
id someValue = [[[myObject foo] bar] baz];
Key-Value-Observing (KVO) allows you to observe changes to a property or value.
To observe a property using KVO you would identify to property with a string; i.e., using KVC. Therefore, the observable object must be KVC compliant.
[myObject addObserver:self forKeyPath:#"foo.bar.baz" options:0 context:NULL];
Key Value Coding is simply accessing a property of an object through a string instead of the literal syntax.
// Here is a new instance of an object
Foo *foo = [[Foo alloc] init];
// Accessing a property called someValue with literal syntax:
[foo someValue];
// Accessing the same property with dot notation
foo.someValue;
// Accessing the same property with Key-Value coding:
[foo valueForKey:#"someValue"];
The power of KVC is that you can specify any arbitrary string at runtime (obviously this could be very dangerous too).
Key-value coding allows you to fetch or change a property of an object using a string, at runtime, instead of needing to write code that is compiled to a fixed property from the start:
NSNumber* foo = [myPopup valueForKey: #"selectedItemIndex"];
[myPopup setValue: #15 forKey: #"selectedItemIndex"];
A good example for this is NSTableView on Mac, where you can just set an identifier on every table column that corresponds to your model object's property that it should display, and then your data source just calls -valueForKey:/-setValue:forKey: with the column's identifier as the key and the values pretty much display/set themselves. You just add the right columns to the table view in the XIB.
Key-value observing was added afterwards, and lets you register to be notified about changes made to another object. You register your interest by doing:
void* gMyKVOContext = &gMyKVOContext; // global variable somewhere that guarantees us a unique address that doesn't collide with a subclass's registration for observing the same property
...
[interestingObject addObserver: interestedObject forKeyPath: #"interestingProperty" options: 0 context: gMyKVOContext];
Whenever that property is changed, -observeValueForKeyPath:ofObject:change:context: will be called on the object you specified as the observer. So you'd implement that like:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if( context == gMyKVOContext && [keyPath isEqualToString: #"interestingProperty"] )
{
// Update UI that shows interestingProperty
}
else
[super observeValueForKeyPath: keyPath ofObject: object change: change context: context];
}
The advantage here is that you get called live the moment that other property is changed. Note that objects have to do a little work so these notifications are sent, so not all properties are key-value-observable. Also note that some objects may be in an invalid state if two related properties get changed right after the other: You get notified after the first property has been changed, which now contradicts the second, and only then the second property is changed and you're notified for that. So during that first observer callback, the object may be in a weird state, so be careful how you react to that.
To make a property observable, either use the default #synthesized implementation when you define it, or if you define it yourself, implement the setter like:
-(void) setFoo: (int)inFoo
{
[self willChangeValueForKey: #"foo"];
_foo = inFoo;
[self didChangeValueForKey: #"foo"];
}
Then always go through the setter to change it, don't change _foo directly. If you have related properties that could contradict each other like the above, a good way to avoid this is to always change them both in pairs (you can't use KVC then, though). To do that, implement a combined setter like:
-(void) setFoo: (int)inFoo bar: (int)inBar
{
[self willChangeValueForKey: #"foo"];
[self willChangeValueForKey: #"bar"];
_foo = inFoo;
_bar = inBar;
[self didChangeValueForKey: #"bar"];
[self didChangeValueForKey: #"foo"];
}
That way, both notifications are sent while the properties are in proper states.
Start here.
Key-value coding is a mechanism for
accessing an object’s properties
indirectly, using strings to identify
properties, rather than through
invocation of an accessor method or
accessing them directly through
instance variables.
Objective-C Key Value Coding(KVC) vs Key Value Observing(KVO)
[Swift KVC]
KVC allows you to access to property by String it is enabled for all NSObject successors. It adds a dynamism in the language. You can consider your class as Dictionary(Key-Value). It is an alternative/not direct solution to assign/read/write variables
[valueForKey vs valueForKeyPath]
B *b = [a valueForKeyPath:#"b"];
KVC is used by KVO, as well as CoreData, Cocoa bindings...
KVO allows you to subscribe on value changed. It is working for dynamic dispatch
someClass.observe(\.v, options: .new) { (object, change) in
//logic
}
[KVO example]