Overriding property setter in a class category - objective-c

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

Related

How to initialize a subclass of PFObject?

So I'm trying to clean up my code and move all of one object's logic to it's own class.
I want to be able to do something like this:
#interface Habit : PFObject<PFSubclassing>
and then be able to call
[Habit calculateStreak].
How would I initialize Habit with an already created PFObject and all of its data? Something like [Habit * running = Habit initWithThis:someObjectWithData]? Or am I thinking of this all wrong?
Subclassing is pretty easy: Just inherit from PFObject and adopt the PFSubclassing protocol:
#interface Habit : PFObject <PFSubclassing>
- (id)calculateStreak;
#end
Then, make sure you override the parseClassName to return the Parse class name:
#implementation Habit
- (NSString *)parseClassName
{
return #"Habit";
}
#end
You also have to register your subclass, which should be done in +[Habit load]:
#implementation Habit
- (void)load
{
[self registerSubclass];
}
- (NSString *)parseClassName
{
return #"Habit";
}
#end
Then you can instantiate the class the way you would any other Parse class, using +[object] or the like:
Habit *habit = [Habit object];

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

Objective-C: Custom BOOL accessor (getter & setter) methods

I know someone already asked about Writing getter and setter for BOOL variable. But, if I'm defining a custom getter & setter methods setImmediate & isImmediate, respectively, I'd like passcode.immediate = NO to work too.
I do not have any instance variables, but maybe I should? I could add one for NSDate *lastUnlocked.
Here's the relevant code so far:
// PasscodeLock.h
extern NSString *const kPasscodeLastUnlocked;
#interface PasscodeLock : NSObject {
}
- (BOOL)isImmediate;
- (void)setImmediate:(BOOL)on;
- (NSDate *)lastUnlocked;
- (void)resetLastUnlocked;
- (void)setLastUnlocked:(NSDate *)lastUnlocked;
#end
// PasscodeLock.m
#import "PasscodeLock.h"
NSString *const kPasscodeLastUnlocked = #"kPasscodeLastUnlocked";
#implementation PasscodeLock
#pragma mark PasscodeLock
- (BOOL)isImmediate {
return self.lastUnlocked == nil;
}
- (void)setImmediate:(BOOL)on {
if (on) {
[self resetLastUnlocked];
} else {
self.lastUnlocked = nil;
}
}
- (NSDate *)lastUnlocked {
return [[NSUserDefaults standardUserDefaults] objectForKey:kPasscodeLastUnlocked];
}
- (void)resetLastUnlocked {
NSDate *now = [[NSDate alloc] init];
self.lastUnlocked = now;
[now release];
}
- (void)setLastUnlocked:(NSDate *)lastUnlocked {
[[NSUserDefaults standardUserDefaults] setObject:lastUnlocked forKey:kPasscodeLastUnlocked];
}
Then, in a view controller that has PasswordLock *passwordLock as an instance variable, I want to do passcode.immediate = NO, but I get the error "Property 'immediate' not found on object of type 'PasscodeLock *'."
How do I get passcode.immediate = NO to work?
You need something like
#property (nonatomic, getter=isImmediate) BOOL immediate;
in your .h file and of course a #synthesize statement in your .m file. This creates the property AND defines your getter method name.
Declare such property in the #interface:
#interface PasscodeLock : NSObject {
}
#property(dynamic, getter=isImmediate,
setter=setImmediate:) BOOL immediate;
// etc.
#end
(The setter= part is optional)
I think the issue ids that your getter and setter names are not consistent. By default, if you have
foo.immediate
in your code, it is assumed that the getter and setter are named -immediate and -setImmediate: respectively. Your getter is not named correctly. The best way around this is to declare a property as Mark and Kenny have already said but you could also change the name of your getter.
The point is that you do not need declared properties to use dot syntax, but if you are going to use dot syntax then declared properties are the recommended way of declaring the getter and setter.

Objective-C Static Class Level variables

I have a class Film, each of which stores a unique ID. In C#, Java etc I can define a static int currentID and each time i set the ID i can increase the currentID and the change occurs at the class level not object level. Can this be done in Objective-C? I've found it very hard to find an answer for this.
Issue Description:
You want your ClassA to have a ClassB class variable.
You are using Objective-C as programming language.
Objective-C does not support class variables as C++ does.
One Alternative:
Simulate a class variable behavior using Objective-C features
Declare/Define an static variable within the classA.m so it will be only accessible for the classA methods (and everything you put inside classA.m).
Overwrite the NSObject initialize class method to initialize just once the static variable with an instance of ClassB.
You will be wondering, why should I overwrite the NSObject initialize method. Apple documentation about this method has the answer: "The runtime sends initialize to each class in a program exactly one time just before the class, or any class that inherits from it, is sent its first message from within the program. (Thus the method may never be invoked if the class is not used.)".
Feel free to use the static variable within any ClassA class/instance method.
Code sample:
file: classA.m
static ClassB *classVariableName = nil;
#implementation ClassA
...
+(void) initialize
{
if (! classVariableName)
classVariableName = [[ClassB alloc] init];
}
+(void) classMethodName
{
[classVariableName doSomething];
}
-(void) instanceMethodName
{
[classVariableName doSomething];
}
...
#end
References:
Class variables explained comparing Objective-C and C++ approaches
As of Xcode 8, you can define class properties in Obj-C. This has been added to interoperate with Swift's static properties.
Objective-C now supports class properties, which interoperate with Swift type properties. They are declared as: #property (class) NSString *someStringProperty;. They are never synthesized. (23891898)
Here is an example
#interface YourClass : NSObject
#property (class, nonatomic, assign) NSInteger currentId;
#end
#implementation YourClass
static NSInteger _currentId = 0;
+ (NSInteger)currentId {
return _currentId;
}
+ (void)setCurrentId:(NSInteger)newValue {
_currentId = newValue;
}
#end
Then you can access it like this:
YourClass.currentId = 1;
val = YourClass.currentId;
Here is a very interesting explanatory post I used as a reference to edit this old answer.
2011 Answer: (don't use this, it's terrible)
If you really really don't want to declare a global variable, there another option, maybe not very orthodox :-), but works... You can declare a "get&set" method like this, with an static variable inside:
+ (NSString*)testHolder:(NSString*)_test {
static NSString *test;
if(_test != nil) {
if(test != nil)
[test release];
test = [_test retain];
}
// if(test == nil)
// test = #"Initialize the var here if you need to";
return test;
}
So, if you need to get the value, just call:
NSString *testVal = [MyClass testHolder:nil]
And then, when you want to set it:
[MyClass testHolder:testVal]
In the case you want to be able to set this pseudo-static-var to nil, you can declare testHolder as this:
+ (NSString*)testHolderSet:(BOOL)shouldSet newValue:(NSString*)_test {
static NSString *test;
if(shouldSet) {
if(test != nil)
[test release];
test = [_test retain];
}
return test;
}
And two handy methods:
+ (NSString*)test {
return [MyClass testHolderSet:NO newValue:nil];
}
+ (void)setTest:(NSString*)_test {
[MyClass testHolderSet:YES newValue:_test];
}
Hope it helps! Good luck.
On your .m file, you can declare a variable as static:
static ClassName *variableName = nil;
Then you can initialize it on your +(void)initialize method.
Please note that this is a plain C static variable and is not static in the sense Java or C# consider it, but will yield similar results.
In your .m file, declare a file global variable:
static int currentID = 1;
then in your init routine, refernce that:
- (id) init
{
self = [super init];
if (self != nil) {
_myID = currentID++; // not thread safe
}
return self;
}
or if it needs to change at some other time (eg in your openConnection method), then increment it there. Remember it is not thread safe as is, you'll need to do syncronization (or better yet, use an atomic add) if there may be any threading issues.
As pgb said, there are no "class variables," only "instance variables." The objective-c way of doing class variables is a static global variable inside the .m file of the class. The "static" ensures that the variable can not be used outside of that file (i.e. it can't be extern).
Here would be an option:
+(int)getId{
static int id;
//Do anything you need to update the ID here
return id;
}
Note that this method will be the only method to access id, so you will have to update it somehow in this code.
(Strictly speaking not an answer to the question, but in my experience likely to be useful when looking for class variables)
A class method can often play many of the roles a class variable would in other languages (e.g. changed configuration during tests):
#interface MyCls: NSObject
+ (NSString*)theNameThing;
- (void)doTheThing;
#end
#implementation
+ (NSString*)theNameThing { return #"Something general"; }
- (void)doTheThing {
[SomeResource changeSomething:[self.class theNameThing]];
}
#end
#interface MySpecialCase: MyCls
#end
#implementation
+ (NSString*)theNameThing { return #"Something specific"; }
#end
Now, an object of class MyCls calls Resource:changeSomething: with the string #"Something general" upon a call to doTheThing:, but an object derived from MySpecialCase with the string #"Something specific".
u can rename the class as classA.mm and add C++ features in it.
Another possibility would be to have a little NSNumber subclass singleton.

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