PFObject Subclass Getters and Setters? - ios

I have a simple PFObject subclass setup. MyPFSubclass. It looks something like this:
#interface MyPFSubclass: PFObject <PFSubclassing>
+ (NSString *)parseClassName;
#property (retain) NSString *myString;
#end
#import <Parse/PFObject+Subclass.h>
#implementation MyPFSubclass
#dynamic myString;
+ (NSString *)parseClassName {
return #”MyPFSubclass”;
}
#end
This works great, as expected, until what I discovered today.
I can set the myString value and read and write as expected, an NSLog shows the data to be what I set it to.
MyPFSubclass *obj = [MyPFSubclass new];
obj.myString = "#hello";
//prints expected value as set above
NSLog(#"%#", obj.myString);
obj[#"myString"] = "#hello";
//prints expected value as set above, again
NSLog(#"%#", obj[#"myString"]);
However, if I do the following, I do not get the changed result.
obj[#"myString"] = #"Hello";
//prints original value, not as set above
NSLog(#"%#", obj.myString);
It seems the key name setters and getters are independent to the subclass setters and getters. I don't want this!
As an example, I have a subclassed view that takes a generic PFObject and key name from which it can get and set values.
How can I resolve this? Any reason why I cannot mix usage of the subclass and keyname getters and setters?

Not an exact answer, however it is a solution.
I found that in the scenario of using a PFObject subclass, using setValue:forKeyPath is more reliable than using Parse's own bracketing syntax. i.e. myObject[#"myAttribute"].
Where this latter appears non-interchangeable with subclass properties, I found replacing it with setValue:forKeyPath works.

Related

Why NSManagedObject Subclass can't use class_copyMethodList find dynamic generated method?

When I create a NSManagedObject Subclass Employee,it has a property nameaccording the EntityDescription in xcdatamodelfile. And in the .m file, the code modify it using #dynamic like this:
#interface Employee (CoreDataProperties)
#property (nullable, nonatomic, retain) NSString *name;
#end
#implementation Employee (CoreDataProperties)
#dynamic name;
#end
According to the Apple's Document:
Core Data dynamically generates efficient public and primitive get and set attribute accessor methods and relationship accessor methods for properties that are defined in the entity of a managed object’s corresponding managed object model. Therefore, you typically don’t need to write custom accessor methods for modeled properties.
According this, I think the CoreData Framework will create two method named name and setName:in the runtime. So I use such code to verify my thinking.
Employee *object = [NSEntityDescription insertNewObjectForEntityForName:#"Employee" inManagedObjectContext:self.managedObjectContext];
object.name = #"1";
[[self class] showInstanceMethod:[Employee class]];
+ (void)showInstanceMethod:(Class)class {
unsigned int outCount;
//..show InstanceMethodList
Method *methodsList = class_copyMethodList(class, &outCount);
for (int i = 0; i < outCount; i ++) {
SEL sel = method_getName(*methodsList);
NSString *methodName = NSStringFromSelector(sel);
NSLog(#"\nmethodName:%#\n", methodName);
methodsList++;
}
}
I'm sad it didn't log any method name like name or setName:.But I use this code object.name = #"1"; and didn't have any problem.
When they say "dynamically" they really do mean it - the dynamic implementation seems to be provided only as and when the selector is called (directly or via valueForKey:). You can see this happening if you override resolveInstanceMethod: in your Employee class. Call the super implementation and log the selector name and return value. Presumably the method will be listed by class_copyMethodList at this point, though I've never checked.

Pass string as tag or other value in button

I need to pass string data with button, like tag, how can I do so?
[button setTag:tg];
tg is nsstring. Maybe use other property and not tag?
Use Associated Objects
#interface UIButton (Tagged)
#property (nonatomic, copy) NSString *tag;
#end
#import <objc/runtime.h>
static const void *tagKey = &tagKey;
#implementation UIButton (Tagged)
- (void)setTag:(NSSting *)tag
{
objc_setAssociatedObject(self, tagKey, tag, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)tag
{
return objc_getAssociatedObject(self, tagKey);
}
#end
Get reference associated-objects and link
You cannot set NSString variable to integer property, it is not possible in Objective-C (if it was an object, technically you could, but you are dealing with a primitive here).
You could use associated properties in Objective-C runtime. You can set any additional variable to any object using this API and then also retrieve it at a later point. See the link below for more information.
http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/
Method objc_setAssociatedObject is the one you are looking for.

Can't set cell title's text

I'm writing simple notes app, but when I try to assign title to note.title I got error.
-(Notez*)createNoteWithTitle:(NSString*)titleNote andText:(NSString*)textNote
{
Notez *newNote = [Notez new];
if (!_notesArray) {
_notesArray = [NSMutableArray new];
}
newNote.dateCreated = [NSDate new];
newNote.title = [NSString stringWithFormat:#"23"];
newNote.text = [NSString stringWithFormat:#"233"];
[_notesArray addObject:newNote];
return newNote;
}
Notez.h :
#import
#interface Notez : NSObject
#property NSString *text;
#property NSString *title;
#property NSDate* dateCreated;
-(Notez*)createNoteWithTitle:(NSString*)title andText:(NSString*)text;
-(void)save;
+(instancetype)sharedManager;
-(NSArray*)sortedNotes;
-(void)removeNoteAtIndex:(NSUInteger)index;
#end
note.dateCreated is OK, but note.title and note.text isn't.
They are both NSString...
- (IBAction)addNote:(id)sender {
[[Notez sharedManager] createNoteWithTitle:#"Note Title" andText:#"Note Text"];
}
Since you declared title and text as properties, but for some reason get the exception:
[Notez setTitle:]: unrecognized selector sent to instance, apparently
I can only make another guess here. Usually, when declaring a property, you get a setter and a getter method for it. This way you can omit writing these by hand if you have a lot of instance variables on a class.
Using dot notation is then equivalent to calling the setter or the getter. So in your case
newNote.title = [NSString stringWithFormat:#"23"];
is equivalent to:
[newNote setTitle:[NSString stringWithFormat:#"23"]];
Now, the exception that you suggests that the setter method: setTitle: actually does not exist on your object. I'm not sure what the reason for this might be, but you could try to explicitly synthesize your properties.
Therefor, in your .m-file add the following lines of code:
#synthesize title;
#synthesize text;
Not sure if this will actually solve your issue, but I hope the explanation of properties, getters and setters helps you to understand a little more what's going on.

Get property class giving the property name

What I need
I have a bunch of classes generated from my data model that inherit from NSManagedObject. I need a way to get, for any of theses classes, a dictionary of a string of a property name as the key, and the string of it's type as the value, for every property name passed in an array.
Or better, in code:
// what I have - generated code
#interface ClassA : NSManagedObject
#property (nonatomic, retain) ClassX *propX;
#property (nonatomic, retain) ClassY *propY;
#end
#implementation ClassA
#dynamic propX;
#dynamic propy;
#end
// what I need
// the method
-(NSDictionary*)dictFromPropNameToPropType:(NSArray*)props {
//dict being something like #{ #"propX" : #"ClassX", #"propY" : #"ClassY" };
return dict;
}
// call
dictFromPropNameToPropType(#[#"propX", #"propY"]);
The logic of the dictionary creation is on me. I need a way to get the property class name as a string, giving it's name.
What I've tried
// a instance method of my ClassA
SEL selector = NSSelectorFromString(propertyNameAsString); // like #"propX"
id object = [self performSelector:selector];
Class class = [object class];
NSString *className = NSStringFromClass(class);
and also tried something with dictionaryWithValuesForKeys, but it seems to use the same mechanism and result in the same kind of errors.
No succes there. I get the "class not key value coding-compliant" error for the key passed in as propertyNameAsString, even thou I have the propX property in my ClassA
What I've researched
I've looked the Apple tutorials about KVC and KVO, and also the one about the Runtime (to learn something about the #dynamic auto-generated property). But didn't figure it out on my own.
Edit
With Martin R's answer, I've got this code:
NSMutableDictionary *result = [NSMutableDictionary new];
NSEntityDescription *selfEntity = [self entity];
NSDictionary *relationshipsByName = selfEntity.relationshipsByName;
for (NSString *relationDescription in relationshipsByName) {
NSRelationshipDescription *relationshipDescription = relationshipsByName[relationDescription];
NSEntityDescription *destinationEntity = relationshipDescription.destinationEntity;
result[relationDescription] = destinationEntity.managedObjectClassName;
}
You have subclasses of NSManagedObject, so you can inspect the objects entity, which is
a NSEntityDescription.
From the entity description, get the propertiesByName, which is
a dictionary with the property names as key. The values are NSAttributeDescription
or NSRelationshipDescription objects.
NSAttributeDescription has a method attributeValueClassName, which is the class
representing the attribute (as a string).
NSRelationshipDescription has a method destinationEntity which describes the target
entity and has a managedObjectClassName method.

Accessing same piece of code from two classes

I have two classes each with an instance method that use the same piece of code.
This piece of code takes a NSString and return an NSArray.
Currently the same piece of code is repeated in the two classes.
Is there a way to write it separately and call it by the two classes? I tried to make a method in a subclass of NSArray, but there are many problems due to the fact that NSArray is an abstract class. Any suggestions?
Thank you.
Instead of subclassing NSArray, the correct approach to extent the behaviour of a class is to create a category on that class.
So, you can create a category on NSString that returns an array, and after you have imported that category to your project, you can call it as if it was part of NSString, for example:
NSString *myString = #"Hello";
NSArray *myArray = [myString generateArrayFromString];
You can find a guide on how to create a category here:
Customizing Existing Classes
You can try to make a NSString category. This category will return the array.
E.g.:
//
// NSString+MyCategory.h
#import
#interface NSString (MyCategory)
-(NSArray *)myMethod;
#end
//
// NSString+MyCategory.m
#import "NSString+MyCategory.h"
#implementation NSString (MyCategory)
-(NSArray *)myMethod {
NSArray *_arr = [self componentsSeparatedByString:#","];
return _arr;
}
#end
Then in your class (or whatever you want in your code) you can import the category:
#import "NSString+MyCategory.h"
and then use it on any string:
NSArray *myArray = [anyString myMethod];
From the sound of it (parsing a string into an NSArray, with on reference to the class's instance fields) you can make the method a class (vs instance) method and invoke it from either class.
Ie:
+(NSArray*)parseThisString:(NSString*)theString {
doSomething;
return result;
}
Invoke with [TheNameOfTheClass parseThisString:inputString].
Of course, if you are reverencing values in the class's instance this won't work.

Resources