Objective-C generic getter methods - objective-c

I need to overwrite the getter method of multiple properties using the getter attribute when I declare my property like the following. I would like the getter of all my properties to be the same method as the code to get those three properties is the same.
#property (nonatomic,strong, getter=getObject) (NSString*) obj1;
#property (nonatomic,strong, getter=getObject) (NSString*) obj2;
#property (nonatomic,strong, getter=getObject) (NSString*) obj3;
Although, I would need, in my getObject method, to know which property is currently being asked. Is is possible in the implementation of the getObject method to know which object is currently being asked? I would like the following %# code to return either obj1, obj2 or obj3.
-(NSString*) getObject{
NSLog(#"the property requested is: %#", ?????)
}
Any ideas on how to do that?
Thanks a lot!
Renaud

This is not possible.
When you define your getter method, the compiler is going to translate requests to myObject.obj3 into simply [myObject getObject]. At that point, you have lost the information about which property was invoked.
You should just define a different getter for each property, and any shared or duplicated code can go into a private method like getObject::
- (NSString *)getObject:(NSString *)propertyName {
// ...
}
- (NSString *)obj1 {
return [self getObject:#"obj1"];
}
- (NSString *)obj2 {
return [self getObject:#"obj2"];
}
// ...

Related

Overriding property setter in a class category

I've got an NSManagedObject class that I want to override a setter to but I've been told it's good practice to not modify the automatically generated class file and create categories to instead extend them (because if you change the model and regenerate the file, you lose all your additions).
If I make a method for the setter in a category, it definitely runs the method (tested with NSLog), but I don't know how to assign the actual property value. Normally, I'd synthesise the property using
#synthesize finished = _finished;
so that I can access the property in the setter using _finished, like so:
- (void)setFinished:(NSNumber *)finishedValue {
_finished = finishedValue;
self.end_time = [NSDate date];
}
but where the property is defined in the NSManagedObject this doesn't seem possible.
You can do with subclassing see the doc here
- (void)setName:(NSString *)newName
{
[self willChangeValueForKey:#"name"];
[self setPrimitiveName:newName];
[self didChangeValueForKey:#"name"];
}
In a category, you can't add property value, only methods. So you will need to subClass in order to perform what you want.
There is an easy way to do so, try the following:
Model.h
#interface Model
#property(nonatomic,copy) NSString * string;
#end
Model + Override.m
#interface Model ()
{
NSString *_string;
}
#end
#implementation Model (Override)
- (void)setString:(NSString *)string
{
return _string = string;
}
#end

Should I use "self" in a block to access class methods?

I have a class to perform little conversions like NSDate to NSString with a specific format, etc.
Every methods are class methods, eg +[Tools humanReadableStringForDate:(NSDate*)date];
I sometime need my method +[Tools A] to call a method +[Tools B] of my class, but inside a block.
Should I create a __block safeSelf = self; or is it unnecessary because I use class level methods ?
EDIT :
here is an example, not my actual code :
#implementation FileManager
+(void) uploadEveryFile:(void (^)(NSObject*))thingToDo :(NSArray*) fileArray {
for(NSString *s in fileArray) {
[[SomeWebAPI uploadFile:s withSuccess:^(NSNumber *responseCode) {
[self logUploadOk:s];
}];
}
}
+(void) logUploadOk:(NSString*)s {
NSLog(#"File upload ok : %#", s)
}
#end
I think this make things clearer. I like to use self keyword even for class methods when I can - in this example I can because I am in the same class and refer to a class level method - because it seems to make more sense, and can be helpful if I have to modify my class name.
So is it correct to write it like this ? Is it working but not really correct ? Do I really need to call logUploadOk using [FileManager logUploadOk:s] ?
Thank you !
It is unnecessary to use __block or __weak or anything like that. You are talking about self in a class method, which is the class object itself. The class object lives for the whole program, so memory management like retain and release on it have no effect. So you don't need to worry about retain cycles.
+ (NSString *)A
{
NSString *something = [Tools B];
NSString *something = [self B]; // both are same inside class method
}
+ (NSString *)B
{
//..
}
That's unnecessary because you're using class method, not instance method. To call Class methods, you use the class name: [Tools doThisForMe].
However, it sounds like you could use Objective-C Categories in this case. It would allow you to extend the different classes and make your code more readable such as [myNSDate humanReadableString].
In your case, it would go along the lines of:
NSDate+Human.h
#interface NSDate (Human)
- (NSString *)humanReadableString;
#end
NSDate+Human.m
#implementation NSDate (Human)
- (NSString *)humanReadableString {
// do whatever you want.
// now 'self' refers to the NSDate instance
}
#end

How to check if a string is an object's instance variable or not?

I have one question about the variables of an object, I would like to know if I can check if a string is an object's instance variable or not?
Below, an example to illustrate my issue :
I have an object - MyObject.h :
#interface MyObject : NSObject
{
//Variables
id myVariable1;
id myVariable2;
}
#property (nonatomic, retain) id myVariable1;
#property (nonatomic, retain) id myVariable2;
And I have also an array list:
NSArray * myArray = [[NSArray alloc] initWithObjects:#"myVariable1",#"myVariable2",#"myVariable3",#"myVariable4",nil];
I would like to know if it's possible to determinate which strings in the array list aren't defined as variable in the object MyObject.
=> myVariable3 and myVariable4 for this case.
I tried to use "isKindOfClass", "isMemberOfClass", "valueForKeyPath", "valueForKey" but without success... Let me know if you have some advices to resolve my problem :)
Assuming the properties aren't using a custom setter name, you could do:
MyObject *object = ...;
for (NSString *name in myArray) {
SEL getterName = NSSelectorFromString(name);
if ([object respondsToSelector:getterName]) {
NSLog(#"MyObject has a method named %#", getterName);
} else {
NSLog(#"MyObject does not have a method named %#", getterName);
}
}
I would create an object to use for comparison using the NSClassFromString class.
if ([myClass isKindOfClass:NSClassFromString(myClassString)] {
// class matches string
} else {
// class doesn't match
}
It's easier to check if values in the array are properties of some object. As in your case the valueForKey: should have worked only if myInstance1 and myInstance2 are non-nil objects. You'd just need to implement - (id)valueForUndefinedKey: method to return nil and all would be fine and dandy.
You can also try using object_getInstanceVariable method while iterating through an array and fetching each possible instance variable separately. I believe that undeclared instances should return NULL pointer (as opposed to nil instances that are declared but undefined).

Objective-C Passing a variable to another IBAction

This works fine:
NSString *myVariable;
- (IBAction) doFirstAction {
myVariable = #"123456789";
}
- (IBAction) doSecondAction {
NSLog(#"%#",myVariable);
}
However, if I do this (substituting the #"123456789" for some code which returns the same value ie "123456789") I cannot access the value in doSecondAction.
NSString *myVariable;
- (IBAction) doFirstAction {
myVariable = [imageNode getAttributeName:#"value"];
}
- (IBAction) doSecondAction {
NSLog(#"%#",myVariable);
}
Any clue as to why I cant access myVariable outside of doFirstAction?
you need to retain it
myVariable = [[imageNode getAttributeName:#"value] retain];
see also
Another way of doing this would be to define an accessor method that does the retaining for you. Thus in your interface definition:
#property (retain) NSString *myVariable;
and later in your implementation:
#synthesize myVariable;
Now Xcode generates getter and setter methods for you that will handle the retain statement for you. However, you now need to use the dot notation, since the equals sign is not overridden, and your setter method is not called unless you do so:
self.myVariable = [imageNode getAttributeName:#"value"];

Can I validate a #property value in Objective-C using #synthesized methods?

What it says on the tin: I'd like to use the #property/#synthesize syntax to define a property on my Objective-C 2.0 class, but I want to place restrictions on the range of values allowed in the property. For example:
#interface MyClass : NSObject {
int myValue;
}
#property (nonatomic) int myValue;
Implementation:
#implementation MyClass
#synthesize myValue(test='value >= 0');
Note that the syntax here is just an example. Is this, or something much like it possible? Alternately, what is the literal equivalent of a synthesized setter, so that I can ensure that I use the same object retention rules in my manual setters as is used in a synthesized one.
Assuming your properties are Key-Value compliant (as they would be if you are using #synthesize) you should also implement Key-Value compliant validators. Take a look at Apple's documentation on the matter: http://developer.apple.com/documentation/Cocoa/Conceptual/KeyValueCoding/Concepts/Validation.html
The important thing to note is that validation does not happen automatically except when using certain kinds of binding. You either call the validator directly or by calling validateValue:forKey:error:.
You could override the produced setter to call the validator before saving it but if you are using bindings this is probably not what you want to do as the validator will possibly be called more than once for a single modification.
Also note that the validator might change the value being validated.
So lets look at your example (untested, btw. I'm not near a Mac):
#implementation MyClass
#synthesize myValue;
-(BOOL)validateMyValue:(id *)ioValue error:(NSError **)outError
{
if (*ioValue == nil) {
// trap this in setNilValueForKey
// alternative might be to create new NSNumber with value 0 here
return YES;
}
if ( [*ioValue intValue] < 0 ) {
NSString *errorString = #"myValue must be greater than zero";
NSDictionary *userInfoDict = [NSDictionary dictionaryWithObject:errorString
forKey:NSLocalizedDescriptionKey];
NSError *error = [[[NSError alloc] initWithDomain:#"MyValueError"
code:0
userInfo:userInfoDict] autorelease];
*outError = error;
return NO;
} else {
return YES;
}
}
If you wanted to override the synthesised setter and make it do the validation (still untested):
- (void)setMyValue:(int)value {
id newValue = [NSNumber numberWithInt:value];
NSError *errorInfo = nil;
if ( [self validateMyValue:&newValue error:&errorInfo] ) {
myValue = [newValue intValue];
}
}
You can see we had to wrap the integer in an NSNumber instance to do this.
When you use the #synthesize the accessor methods are generated. You can implement your own which will overwrite the generated one.
You can put your own implementation inside the accessor methods, e.g. you can add value checking before assignment and so on.
You can ommit one or the other or both, the ones that you don't implement will be generated because of #synthesize, if you use #dynamic you are specifying that you will provide accessors either at compile or run time.
Accessors will have names derived from the property name myproperty and setMyproperty. The method signatures are standard so it is easy to implement your own. The actual implementation depends on property definition (copy, retain, assign) and if it is read-only or not (read-only doesn't get set accessor). For more details see objective-c reference.
Apple reference:
#synthesize You use the #synthesize
keyword to tell the compiler that it
should synthesize the setter and/or
getter methods for the property if you
do not supply them within the
#implementation block.
#interface MyClass : NSObject
{
NSString *value;
}
#property(copy, readwrite) NSString *value;
#end
#implementation MyClass
#synthesize value;
- (NSString *)value {
return value;
}
- (void)setValue:(NSString *)newValue {
if (newValue != value) {
value = [newValue copy];
}
}
#end