I'm trying to save some data into database using CoreData so I created Entity named 'Client' with some attributes. Two of them are 'city' and 'post_code', both of String type. I also created Client class extending NSManagedObjects and I wrote some methods there.
-(void) setCity: (NSString*) city
{
[self setValue:city forKey:#"city"];
}
-(NSString*) getCity
{
return [self valueForKey:#"city"];
}
-(void) setPostCode: (NSString*) postCode
{
[self setValue:postCode forKey:#"post_code"];
}
-(NSString*) getPostCode
{
return [self valueForKey:#"post_code"];
}
getPostCode and setPostCode work as I expected but calling setCity or getCity is causing problems. Appication stops and method is looping in thread as you can see on screenshot.
Full size image
This is how I call those methods
if([databaseResult count] > 0)
c = [databaseResult objectAtIndex:0];
else
c = [NSEntityDescription insertNewObjectForEntityForName:#"Client" inManagedObjectContext:context];
[c setPostCode:[jsonData valueForKey:#"post_code_invoice"]];
[c setClientType:[jsonData valueForKey:#"company_type"]];
[c setCity:[jsonData valueForKey:#"city_invoice"]];
it always stops on setCity no matter what data I pass there, even that call doesn't work
[c setCity:#"aaa"];
Did anyone had similar problem?
The setValueForKey method is calling back into the same function because it has the same name.
From the KVC documentation
Default Search Pattern for setValue:forKey:
When the default implementation of setValue:forKey: is invoked for a property the following search pattern is used:
The receiver’s class is searched for an accessor method whose name matches the pattern set:.
If no accessor is found, and the receiver’s class method accessInstanceVariablesDirectly returns YES, the receiver is searched for an instance variable whose name matches the pattern _, _is, , or is, in that order.
If a matching accessor or instance variable is located, it is used to set the value. If necessary, the value is extracted from the object as described in “Representing Non-Object Values.”
If no appropriate accessor or instance variable is found, setValue:forUndefinedKey: is invoked for the receiver.
So when you call setValue:forKey: with the key city, the implementation calls setCity: and your implementation calls setValue:forKey:, and round and round you go.
Why are you even doing it this way rather than setting the value directly?
Or better still use properties and you don't even need to write setters or getters.
The other two methods work because the key names are different. (they have underscores
)
Related
Feel lost after reading this section: A Non-Object Attribute
According to the Basic-Approach also contained in above link, I should have 2 attributes in my custom-code when handling "transient properties":
1st attribute, for the actually-wanted (un-supported) custom type => transient attribute
2nd attribute, for shadow-representation (concrete supported) type => persistent attribute
......
My reading was very enjoyable, until reached "A Non-Object Attribute" section, which puzzle me deeply, as quoted below:
...When you implement the entity’s custom class, you typically add an instance variable for the attribute. ...
《 OK, I can follow this...make an iVar is no big deal》
If you use an instance variable to hold an attribute, you must also implement primitive get and set accessors
《 OK, I know how to do primitive-accessor. why need them? because internal-optimized-storage inside MO can be efficiently used, I guess.》
#interface MyManagedObject : NSManagedObject
{
 NSRect myBounds; // I assume this suppose to be the **transient attribute**
}
#property (nonatomic, assign) NSRect bounds; // I assume this is the **persistent attribute**
#property (nonatomic, assign) NSRect primitiveBounds; // because complier forces me to implement below primitive-accessors ?
#end
- (NSRect)primitiveBounds
{
return myBounds; // accessing iVAR storage for **transient attribute**? I hope so
}
- (void)setPrimitiveBounds:(NSRect)aRect
myBounds = aRect; // accessing iVAR storage for **transient attribute**? I hope so
}
From here down below, I have... too many ???????????? unsolved
- (NSRect)bounds
{
[self willAccessValueForKey:#"bounds"]; //KVO notice of access **persistent attribute**, I guess
NSRect aRect = bounds; //will this invoke primitive-Getter ???
[self didAccessValueForKey:#"bounds"];
if (aRect.size.width == 0) //bounds has not yet been unarchived, Apple explained
 {
NSString *boundsAsString = [self boundsAsString]; // unarchiving pseudo method, I guess
if (boundsAsString != nil) //if that value is not nil, transform it into the appropriate type and cache it...Apple explained.
{
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
}
}
return bounds;
}
I put my final question list here:
1, do I STILL need to have 2 attributes to handle NON-Object-Attribute, transient attribute and persistent attribute?
2, how can iVar "myBounds" be represented/connected with "#property bounds"? Is this "#property bounds" the modeled-property in a MOM?
3, what is the purpose of implementation of primitive-accessor here? for enforcing me write KVO (will...did...) methods pair? for transferring values (in and out) between iVar "myBounds"and "#property bounds"?
4, in this line of code
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
is primitive-Setter called OR public/standard-Setter gets called? Why?
In iOS, there are the very convenient NSStringFromCGRect and CGRectFromNSString functions. Why not just use those and store a string?
Your questions:
Yes, you need the 2 attributes, as explained in the documentation.
Yes, this is based on the managed object model. The primitiveX name for x is generated / interpreted automatically.
You need the primitive accessor methods here to make it KVC - which is not the case with primitives.
Rather than a long if statement, what is a more compact and readable way to verify if a string is contained in a collection of possible values? In other words, check if a value is within a domain?
I want to do something like this…
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
BOOL valueFoundInDomain = [domain containsObject:#"elephant"];
But I'm concerned about equality checking with NSString. I want to check the value of the text, not object identity.
The documentation for NSArray says the containsObject method uses the isEqual method. But I cannot find in the documentation for NSString an explanation for its implementation of isEqual. The presence of the isEqualToString method suggests that isEqual may be doing something else. If that something else involves interning of string objects, then experimenting myself may give misleading results, so I'd like a documented answer.
I never use -isEqualToString:, only -isEqual: and it just works as it should! (I do this for years.)
NSString is implementing -isEqual: and it returns YES if the other object is a string and it has the same contents.
In Apples Objective-C documentation, methods that are overridden from a baseclass are often not explicitely documented. But -isEqual: is one of the few methods that is implemented in all foundation classes where it makes sense.
The isEqual method does an additional type check to ensure you are comparing two objects of the same class.
IsEqualToString assumes you are sending a string and will crash if you send a nil or object of another type.
Your code looks good for its use case.
Lets Try Using This
NSArray* domain = [NSArray arrayWithObjects:#"dog", #"cat", #"bird", nil];
NSIndexSet *indexes = [domain indexesOfObjectsWithOptions:NSEnumerationConcurrent passingTest:^BOOL(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
return [obj isEqualToString:#"elephant"];
}];
// Where indexes contains matched indexes of array elements
Here isqualToString: Returns a Boolean value that indicates whether a given string is equal to the receiver using a literal Unicode-based comparison.isEquealTo: Returns a Boolean value that indicates whether the receiver and a given object are equal. When you know both objects are strings, isEqualToString: is a faster way to check equality than isEqual:
I have an NSArray and I need to change the order of the items within it. I have written a method that will determine the new order:
+(NSArray*)sortProxyForms:(NSArray*)arrayOfForms
{
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
arrayOfForms = [sortedForms copy]; // DOES NOT WORK
return sortedOfForms; // WORKS IF ASSIGNED IN THE CALLER
}
So, I can pass the NSArray to be sorted into the method. I call the method like this:
[HPSModelUtilities sortProxyForms:_formProxies];
If I actually try setting arrayOfForms (a reference to _formProxies) within the method then once I have returned from the method then the array is unchanged.
However, if I return the sorted array from the method and assign it to the NSArray in the calling method then the assignment works:
_formProxies = [HPSModelUtilities sortProxyForms:_formProxies]; // _formProxies NSArray is changed
_formProxies is declared in the calling class, and "HPSModelUtilities" is a different class.
How come the NSArray can be changed in the caller, but not changed in the called method, even though it is passed by reference?
When you pass a value into a method it is copied. This is called "pass by value". The arrayOfForms you are passing in is a pointer to an NSArray. This means that the pointer is copied when passed in. Redirecting this pointer to another instance of an NSArray does not change where the original pointer is pointing.
I would rename your method to (NSArray*)sortedArrayFromProxyForms:(NSArray*)proxyForms
If you really want to change where your NSArray reference is pointing in the method. Do it like this.
+ (void)sortProxyForms:(NSArray**)proxyForms {
*proxyForms = sortedForms;
}
You are passing a copy of the array reference (subtly different than passing by reference), but then you are changing where that reference points with this line:
arrayOfForms = [sortedForms copy];
arrayOfForms no longer points to the array instance you passed, but to a different array. You could pass a pointer of pointer, and change where the caller's pointer is pointing, but for what you are doing, I think the reassignment is fine.
If you'd really like here's what your function would look like with pointer of pointer:
+(void)sortProxyForms:(NSArray**)arrayOfForms {
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
*arrayOfForms = [sortedForms copy];
}
but I'll add the caveat that this isn't a pattern you see often in objective-c, so I'd avoid it when there are other alternatives available.
Also note when calling this function you need to add the & to get the extra level of indirection:
[HPSModelUtilities sortProxyForms:&_formProxies];
So in a regular Class, you can override the setter method for a class property:
-(void)setSortBy:(NSString *)sortBy {
// Do other stuff
_sortBy = sortBy;
}
using the _ prevents an infinite loop of the method calling its self.
I am trying to do something similar with a NSManagedObject class, but it does not give the option to use the underscore (_):
-(void)setHasNewData:(NSNumber *)hasNewData {
// update self.modifiyDate
_hasNewData = hasNewData;
}
Gives me an error and suggests I replace _hasNewData to hasNewData.
Is this how it should be done or will it give me an infinite loop?
I want it to update the NSManagedObject's property modifyDate anytime I set hasNewData.
Your first example for a "regular class" works if _sortBy is the instance variable
backing up the sortBy property (e.g. the default synthesized instance variable for
that property).
But Core Data properties are not backed up by instance variables.
When overriding Core Data accessors, you have to use the "primitive accessors",
and also trigger the Key-Value Observing notifications:
-(void)setHasNewData:(NSNumber *)hasNewData {
[self willChangeValueForKey:#"hasNewData"];
[self setPrimitiveValue:hasNewData forKey:#"hasNewData"];
[self didChangeValueForKey:#"hasNewData"];
// do other things, e.g.
self.modifyDate = ...;
}
More examples can be found in the "Core Data Programming Guide".
This may be a ridiculous question, but I have a method like this in my view controller:
[self registerProperty:self.currentUser];
and in the implementation of registerProperty: I would like to get the string "currentUser".
I'm doing this because I want to observe the property of the view controller "currentUser", not the actual user object, so I can intercept the setter.
At the moment I'm checking the Objective-C runtime for a list of all properties of the view controller and checking if the value of the property equals the currentUser object:
-(void)registerProperty:(id)property
{
for (NSString *propertyName in [self allPropertiesOfClass:[property class]])
if ([property isEqual:[self valueForKey:propertyName]])
NSLog(#"The property passed into the method is %#", propertyName);
}
The problem with this is that I may have two properties that both contain the same user object, in which case either of them would pass that test. How could I fix this?
Pass in the object whose property you want to observe and, separately, the property name as a string. That is, mirror (a subset of) the arguments of the KVO -addObserver:... method.