I have a category on NSDate, and I want to implement some functions to manipulate the date, like :
NSDate *thedate = [NSDate date];
[thedate setToMidnight];
so I have a function in NSDate like :
-(void)setToMidnight {
some code with calendars and comps
self = theNewDate;
}
This works inside the function, but outside this member function, thedate has not changed.
I understand this malfunction because I've been told that self is just a local variable created inside the member function.
So, how can I make this work ?
Of course, I could have written :
thedate = [thedate dateAsMidnightDate]
or thedate = [NSDate dateAtMidnightFromDate:thedate]
but I feel it has more sense inside the instance class, as I don't want to change the date but just adjust some values of the previously created one.
You can't. Specifically:
A function or method cannot modify local variables in the calling context.
NSDate is not mutable (i.e. you can't modify an NSDate once it's created).
Therefore, no such method can be written. The closest you can get would be a class that wraps an NSDate and forwards messages to that internal NSDate object, which reassigns the date instance variable when you want to make it represent a new date.
Related
I am following a book for objective-C and came across this example to print the current date using NSLog statement. I am confused why the NSDate class was not instantiated(alloc and init) before passing the date message to it.
NSDate * pointerToIt = [NSDate date];
Further down the code another message was passed to this pointer..
[pointerToIt timeIntervalSince1970];
What I knew is as long as the pointer holds the address to instance of a class messages can be sent to it but the class was never instantiated and still the messages are being passed. Can someone throw some light on this for me ?
The date is a somewhat special way to get the current date. It is a static method on the NSDate class which does the following:
Creates and returns a new date set to the current date and time.
This method uses the default initializer method for the class, init.
Your code is pretty much the same as in the docs:
NSDate *today = [NSDate date];
Therefore the today object is in fact implicitly correctly initalized.
[NSDate date] did the alloc and init for you. If you refer to the documentation you will read.
Creates and returns a new date set to the current date and time.
[NSDate date] is a class factory method. Internally, it would look something like this:
+ (instancetype)date {
return [[self alloc] init];
}
I am learning Objective-C and I've just read about alloc and int methods. Before this point,when I wanted to create an instance of NSDate for example, I coded:
NSDate *now = [NSDate date];
Now I saw that the above can be written like this
NSDate *now = [[NSDate alloc] init];
Are the above do the same thing? As i have understood (hopefully correct) the first one creates and instance of NSDate, by sending the message date to the class NSDate. The second one, it just allocates space for the instance and initialize it, so that it is ready to work.
You would think the two things you list were definitely not the same thing based on a background knowledge of Objective-C, if you weren't familiar with NSDate. But in fact, they are the same thing in this case.
[NSDate date] is calling an NSDate class method that returns an NSDate object set to the current date and time.
Normally, a method call like [[NSDate alloc] init] would instantiate a new default object of the type requested, so you might expect that this would not be set to any date/time. However, the default NSDate object is in fact initialised with the current date and time, as discussed in the documentation, so in this particular case—they are the same thing.
As an aside, as with most NSObjects, you can also just call [NSDate new] to get the same effect as [[NSDate alloc] init] (and thus the same effect in this case as [NSDate date]).
I'm studying Objc with the book from BigNerdRanch 'Objective-C Programming from Aaron Hillegass' and there's this thing that keeps puzzling me.
I understand that the complier needs to know what kind of variable I'm talking about so i have to declare the var type before assigning a value.
int myNum = 10;
Fine. But when it comes to ObjC classes, what's the reason for declaring the type of pointer if I have to declare it right after the =, when I alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
Obviously the *dateFormatter object is an istance of NSDateFormatter, I wrote it in the allocation. Why do i have to declare it at the beginning too?
'Cause if I try to do something like
NSString *timeZone = [NSTimeZone systemTimeZone];
Xcode clearly warns me with 'Incompatible pointer types initializing 'NSString *' with an expression of type 'NSTimeZone *'.
I feel like I'm missing something. Sorry if it's a dumb question, trying to learn.
Here the real question is rather "Why do I have to define the correct class of the pointer?"...
The answer is: you may want to use the variable in some other context as well. If you message [NSTimeZone systemTimeZone] directly, then the compiler may be able to deduce the type, but what if you message the variable? If you go with the weaker-typed
id tz = [NSTimeZone systemTimeZone];
then there's much less opportunity for the compiler to check for errors if you use tz where an NSTimeZone * is expected than it could if you declared it as NSTimeZone *tz.
As an even clearer example, suppose you have a method:
- (NSTimeZone *) userSpecifiedTimeZone {
id timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName;
return timeZone;
}
See the bug?
Xcode won't catch it, since it's perfectly valid to assign any object to a variable of type id, and to return an id from a method whose return type is any object type, and to subsequently assign that id to another id variable, or try to send messages to it, in the calling code.
You'll find this bug—if you don't catch it early with your own human eyes—only at run time, and only when the user enters a bogus time zone name, and you then try to use that time zone name (wrongly returned as this method's result) as an NSTimeZone object.
Compare to the statically-typed version:
- (NSTimeZone *) userSpecifiedTimeZone {
NSTimeZone *timeZone = [NSTimeZone timeZoneWithAbbreviation:self.timeZoneName];
if (timeZone == nil)
timeZone = [NSTimeZone timeZoneWithName:self.timeZoneName];
if (timeZone == nil)
timeZone = self.timeZoneName; //Clang calls shenanigans here
return timeZone;
}
Clang rightly objects that assigning an NSString * to a variable typed as NSTimeZone * is suspicious.
You don't have to define the class of the pointer, but the potential otherwise for bugs like the one shown above is why we do it.
But when it comes to ObjC classes, what's the reason for declaring the
type of pointer if I have to declare it right after the =, when I
alloc and init it?
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
You're not declaring the type of pointer twice. There's a lot going on in this statement. The first occurrence of 'NSDateFormatter' is telling the compiler that dataformatter is a pointer to this type of object, whereas the second occurrence of 'NSDateFormatter' is calling the 'alloc' method in the NSDateFormatter class. Same word, two completely different meanings.
The first thing that happens is [NSDateFormatter alloc] which is calling the (class) method 'alloc' in the 'NSDateFormatter' class. This returns an (empty) instance of an NSDateFormatter object in which the method 'init' is called. A pointer to the resultant object is then stored in your 'dateFormatter' variable, and we tell the compiler that this is a pointer to an NSDateFormatter object.
Think of it like this:
NSDateFormatter *dateFormatter; Create a pointer to an NSDateFormatter object.
newDate = [NSDateFormatter alloc]; Create an empty NSDateFormatter object by calling the class method alloc in NSDateFormatter
[newDate init]; Initialise it by calling the onject's 'init' method
dateformatter = *newDate; Assign a pointer to it to my variable.
There's a rule that says
Only mock objects that you own.
I think I understand the reason for this - mocking classes supplied by the frameworks might result in strange behaviour.
What's the alternative?
What about when you need a dummy date using NSDate?
In the past I've swizzled the date method from NSDate to my own class - NSDateMock - but something tells me that's really wrong!
One Solution - A Wrapper?
Create a wrapper round NSDate, but then you'd have to implement all it's methods.
Or would you just implement the ones you were using? It seems a messy way of doing it.
My Question
What's a good way of mocking classes you don't own such as NSDate?
Update 1
I found this article on mocking which seems to imply that writing a thin wrapper is the way to go. I'm not quite sure why but I feel like this is a hack. Then again, it could make the code more expressive.
But that raises the question, in the case of NSDate do you inject the wrapper class into every single class that needs to know the date?! Surely not...
Update 2
There have been some good answers on this question, but I'm still holding out for other answers - there must be a definitive way of doing this, surely? I still don't see how categories are going to give me a dummy object I can control either.
In general this is a "how to mock a static/class level method" question, which googling shows a lot of different ideas, so it mainly comes down to taste.
I consider partial mocks to be a code smell as it shows that your class under test (CUT) isn't testable -> time to refactor.
I've tackled this in 2 ways in the past:
1.) Pass a DateProvider (which is an interface) into the constructor of the CUT
interface DateProvider
date timenow()
end
At test time this is your MockDateProvider which you can change the state from your test class. I might use a public static field that I can change in my tests
class MockDateProvider :: DateProvider
public static field fakeDate
date timenow ()
return fakedate
end
end
In the real system it just has the date creation method you used.
class RealDateProvider :: DateProvider
date timenow()
return LibraryDateMaker.newDate()
end
end
I might make 2 constructors, one that takes this interface and another which uses the RealDateProvider without any production classes needing to pass in the object.
This is the preferable OO way of doing things. I think!
2.) Make your own static date provider that you can override the behaviour of.
Rather than LibraryDateMaker.newDate() you make a ConfigurableDateMaker.newDate() static. This uses the same objects as 1, but has a setter to allow you to change the behaviour to the mock provider as required. Defaulting to real.
The advantage of this is you don't have to pass anything into your constructors and you get to keep using a static method for a very common activity.
In brief, the CUT calls ConfigurableDateMaker.newDate(). Which by default returns a real date, but in your test class you can set the behaviour to use the mock before calling the CUT.
class ConfigurableDateMaker
public static DateProvider provider
static date timenow()
return provider.newDate()
end
// add the 2 provider classes as inner classes in here
end
Hopefully that makes some sense.
You could instead create a category of NSDate and implement an additional method such as
[NSDate mockDate];
There is no need to create a whole new class.
I'm not sure if this gets at what you are trying to do, but when I've run into similar situations I've done something like the following:
Whenever I have a class that depends on some class that I might want to later mock out,
I encapsulate access to that class within a method of my class. For example, instead of
calling [NSDate date] to get current time, I would implement a method called currentTime.
The implementation of that method would simply return [NSDate time].
I execute my test against a "partial mock" object created from my object under test,
stubbing the currentTime method to return my preferred test value rather than allowing
the real currentTime implementation from getting invoked.
I am using the OCMock framework to do all this, so it looks something like:
NSDate *testDate = // put whatever you want here
id mockTestObject = [OCMock partialMockForObject:testObject];
[mockTestObject stub] andReturn:testDate] currentDate];
[mockTestObject doSomethingThatUsersCurrentTime];
[mockTestObject verify];
OCMock provides a lot of different variants for stubbing, and I've found it works very well for this kind of thing.
What specific problem are you trying to solve by mocking NSDate? In general, it should never be necessary to mock a value object like NSDate. Its interface provides easy ways to create an object representing any given date/time.
You could create a convenience method in your test class for generating the dates you need:
-(NSDate *)dateFromString:(NSString *)dateString {
// short style is 2/20/12 10:05 AM
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterShortStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
return [formatter dateFromString:dateString];
}
-(void)testTimer {
NSDate *startTime = [self dateFromString:#"2/20/12 11:58 PM"];
NSDate *endTime = [self dateFromString:#"2/21/12 12:02 AM"];
// pass dates into your timer method
}
Alternatively, if you need to test a method that gets the current time directly, you could provide a thin wrapper around [NSDate date]:
-(NSDate *)now {
return [NSDate date];
}
-(void)startTimer {
self.startTime = [self now];
}
-(void)stopTimer {
NSDate *endTime = [self now];
// ... do something
}
Then, in your test, you mock your thin wrapper with a partial mock:
-(void)testStopTimer {
Timer *timer = [[Timer alloc] init];
timer.startTime = [self dateFromString:#"2/20/12 11:58 PM"];
id mockTimer = [OCMockObject partialMockForObject:timer];
[[[mockTimer stub] andReturn:[self dateFromString:#"2/21/12 12:01 AM"] now];
[timer stopTimer];
// verify expected behavior...
}
Q: How to mock a class you don't own?
A: Just like a class you own.
Example: Mocking NSDate:timeIntervalSince1970 to return a known value
Staying away from opinion-based responses, and focusing on a solution, say that you have the following invocation that you want to control in order to return a known value:
[[NSDate new] timeIntervalSince1970]]
…you can mock the new method to return anything you want, including nil †.
The following example first creates a NSDate with 42, used in the OCMStub:
// Prepare a known date
NSDate * life = [NSDate dateWithTimeIntervalSince1970:42];
// Mock the entire NSDate class, and stub `new`
id mockDate = OCMClassMock([NSDate class]);
OCMStub([mockDate new]).andReturn(life);
NSLog(#"%f",[[NSDate new] timeIntervalSince1970]); // "42.000000"
[mockDate stopMocking];
Notes
Do not forget stopMocking!
(†) This is probably why it is recommended to
only mock objects that you own.
Below is a code sample from Apple's iOS Core Data tutorial and I thought it was weird that the conditional statements are checking if the object is nil. Wouldn't the object always evaluate to nil if the line before the conditional sets the object to nil?
// A date formatter for the time stamp
static NSDateFormatter *dateFormatter = nil;
if (dateFormatter == nil) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
}
Because of the static. This variable is not set to nil whenever the execution passes through that statement, it 's only set on program startup.
That's a feature of static storage duration variables. They're set to their initialised value at startup and retain whatever value you set them to after that. For example, the following code:
void plugh(void) {
static int xyzzy = 0;
printf (" %d", xyzzy); // or Obj-C equivalent.
xyzzy++;
}
will not output a long string of zeros if you call it a hundered times. It will output:
0 1 2 3 4 ...
In th case of the Apple code, it means the date formatter will be created on demand and (unless you set it back to nil somewhere else) only once. This can someties be important for performance if the object creation is a non trivial thing but, even if not, there's no point in continuously recreating something you can simply re-use.
I'm assuming this code is from the body of a function. You need to note that the variable is static. That means yes, the first time this function is called, it will be set to nil. However, the next time the function is called, it retains its value from the previous call.
So the result of this is lazy initialization. A new NSDateFormatter is initialized only the first time this function is called.
static essentially makes it a global variable, initialized to that value, but is visible only to the function it is declared in.
The "static" means it only evaluated once, upon initializing that class and on the first run through. Subsequent runs through will skip right over it and continue to the next line.