I'm still little bit perplexed by pointers and memory management (starting out with ObjC and Cocoa). What got me thinking, is this piece of code:
double seconds = [[NSDate date] timeIntervalSince1970];
This is what I understand:
I get value of float type returned from calling method/message on NSDate class
This value gets stored in variable seconds
What I don't understand is am I creating NSDate object (=instance of class NSDate) at all? Is this object only temporary?
I always thought that the way to create an object and have the object to be persistent (at least until ARC steps in or it is destroyed when function ends) is to create a pointer to it. Maybe like this:
NSDate *now = [NSDate date];
[now timeIntervalSince1970] // get the value
Does this mean that in my original example, there is some unnamed (no variable pointing to it) instance of NSDate created on the heap and once it returns the float value it gets removed from heap?
Does this mean that in my original example, there is some unnamed (no
variable pointing to it) instance of NSDate created on the heap and
once it returns the float value it gets removed from heap?
Yes, that's exactly right.
ARC will remove the object when it goes out of scope.
Even before ARC, the object would be created as autorelease and be released the next time through the event loop (or whenever the nearest autorelease pool was drained).
Related
I'm creating tests where I have to make sure 2 different NSDate instances are really two different instances of allocated memory. So I have this example code:
NSDate *date1 = [NSDate date];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:0 sinceDate:date1];
XCTAssertEqualObjects(date1, date2);
XCTAssertNotEqual(date1, date2);
The first assert should compare object values using "isEqual", and it's working great!
The second assert should compare pointers using "==". The bizarre thing is that it sometimes fails randomly, telling me that both pointers have the same value (ie, they are pointing to the same allocated memory).
As I'm allocating twice, it is supposed to be different memory areas... So why do I have this test failing randomly sometimes? Maybe XCode is reusing memory areas somehow?
You can't reliably force the creation of separate objects. Some classes may use tagged pointers. The set of classes doing that can change over time with releases of the OS. A tagged pointer really just encodes the value of the object into a pointer-sized value. It doesn't allocate any memory. By definition, any two objects represented as tagged pointers whose values are equal will have equal "addresses".
Also, an init method is just a method. It can return any object it wants. There's no rule that it has to return the receiver. It can release the alloced object it is sent to (self) and return a different object. If it can determine that an existing object (such as the parameter you're passing to -initWithTimeInterval:sinceDate:) meets its needs, it may return that object (with an extra retain). This sort of thing is common in immutable value classes, like NSDate or NSString.
You're going to have to reconsider your supposed need to "make sure 2 different NSDate instances are really two different instances of allocated memory".
I'm currently reading The Big Nerd Ranch Guide on Objective C programming and I'm having trouble understanding a section in Chapter 13, Objects which is providing bad examples of Messages:
NSDate *now = [NSDate date];
double seconds = [now timeIntervalSince1970];
This is firstly a correct example of using messages in Objc. The first line creates a pointer to an NSDate object using the variable now and the NSDate type declaration. It is now an instance of NSDate. It was explained to me that if you are to print now, it will display the output of the NSDate date method. This is where things start to get a little weird for me. It seems odd that you would have a variable pointing both to an instance, and a function output if printed. But that's okay. Moving on.
The second line creates a variable called seconds, which is of type "double" and is going to obtain a value that the timeIntervalSince1970 method of NSDate instance now outputs.
Here is where the author introduces examples of bad messages:
double testSeconds = [NSDate timeIntervalSince1970];
NSDate *testNow = [now date];
He explains the errors as follows:
First Line:
"The Error is clear, the receiver in this message send is the NSDate
class, so the selector should be the name of an NSDate class method.
This selector is not."
Ok. This makes sense. An invalid method. Next. .
Second Line:
"This error is less clear: It is telling you that NSDate has no
instance method whose name matches the date selector."
Wait, what? Isn't now a pointer to an instance of NSDate? Shouldn't you be able to call the date method from an instance of NSDate? I don't get it.
The book does not explain any more than what I've quoted above, so I'm sure it's something stupid and basic I'm not getting. I hope this isn't too specific or unhelpful to others. I'll delete the submission if asked. Thank you.
The variable now points to an instance of NSDate. The method date is not defined on instances of NSDate but on the class itself. Hence date is a class method and not an instance method. Unlike other programming languages Objective-C does not inherit class methods to their class instances.
Vice versa instance methods cannot be called on classes. This said timeIntervalSince1970 cannot be called on the class NSDate as this method is an instance method. This is due to the circumstances that instances usually manage an instance state. Instance methods do operate on this instance state, namely reading and modifying their instance variables. Back to your example: The instance method timeIntervalSince1970 calculates the difference between 01/01/1970 and a concrete, instantiated date. So if you would be able to call timeIntervalSince1970 on class level there's no chance to calculate a difference of dates as the class NSDate doesn't carry any date information (the instance state!) such as the day, month, year and the time.
To sum up: Instance methods cannot be called on classes. This isn't supported by any programing language I am aware of. Calling class methods on instances is however supported by some programing language although there's typically no need for doing so and sometimes it even lowers code readability. However Objective-C doesn't support those calls neither.
This question already has answers here:
alloc and init what do they actually do
(5 answers)
Closed 9 years ago.
NSDate* now = [[NSDate alloc] init];
Currently learning Objective C and my book doesnt seem to do a good job of explaining this line of code. So, i'm aware that we are declaring a pointer "now" that points to an NSDate object. The message is what seems to be confusing me.
If i'm correct, [NSDate alloc] is allocating some memory for an instance of NSDate, but what is init doing?
You are correct about alloc
It allocates memory for an instance of NSDate
init does what it sounds like. It initializes this newly allocated memory. At this point you don't really need to know what init does internally.
Keep learning and when you get to the topic of creating a custom class or subclass, the role of init will become more clear.
it's very simple ...
NSDate* now = [[NSDate alloc] init];
1.alloc allocates memory for the instance of NSDate i.e now in your code
2.init initializes your instance variable with some default value . if don't use init then your instance may contain some garbage value , to avoid that we usually initialize object with some default value .
Please note that update 3 is probably most relevant
Im setting a NSTimeInterval property of a managed object with an nsdate object using setValue:forKey:
When i attempt to get the value I get weird stuff, at runtime this
NSLog(#"[managedObject valueForKey:#\"startTime\"] : %#, [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: %#",
[managedObject valueForKey:#"startTime"],[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]);
Returns
[managedObject valueForKey:#"startTime"] : 2012-07-14 08:13:05 +0000,
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: 1981-07-14 08:13:05 +0000
Update 1
The value returned by [managedObject valueForKey:#"startTime"] is correct. However I would prefer to use [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] or something similar so that it is more strongly typed.
I believe [managedObject startTime] returns an incorrect value => 363954111.000000 .
However i set it with something like this:
managedObject setValue:1342261311 forKey:#"startTime"
It is worth noting that I am unsure whether this is incorrect because [managedObject valueForKey:#"startTime"] returns a correct NSDate object.
Update 2
I've logged the double values returned by KVC and . syntax.
managedObject.startTime = 363954111.000000
valueForKey timeIntervalSince1970 = 1342261311.000000
Update 3
Okay, I've set up a test, start time is set like this entity.startTime = [[NSDate dateWithTimeIntervalSince1970:1342261311] timeIntervalSince1970]; and end time is set like this [entity setValue:[NSDate dateWithTimeIntervalSince1970:1342261311] forKey:#"endTime"];
When i write them to log i get this start = 1342261311.000000, end = 363954111.000000
It seems that the NSDate object is being unwrapped incorrectly, has anyone seen this before?
The problem here is that valueForKey: is intended to be used with object values, in fact it returns an id.
As a convenience, valueForKey: wraps primitive types (such as integers and doubles) in their NSNumber counterparts.
The reason you see two different values is that valueForKey: returns an id, which essentially is a pointer to the position in memory where the NSNumber happens to be stored. Your code then just takes this arbitrary memory address and somehow interprets it as a double and then constructs an NSDate out of that.
Calling the startTime accessor method directly, on the other hand, returns the double without any further ado.
If you want to use valueForKey:, you can do something like this to get the real value:
NSTimeInterval tiv = [[managedObject valueForKey:#"startTime"] doubleValue];
and then work from there.
I am actually a bit surprised that the compiler doesn't emit a warning about this. Apple's latest compilers have become quite adept at catching problems like this one.
It was a problem with the difference in epochs. NSDate uses Jan 1 2001 as an epoch. So when I was getting the value I was using the unix epoch (1970). That gave me a difference in values.
When KVC unwraps and wraps NSTimeInterval with a NSDate object it uses the NSDate 2001 epoch.
So instead of using dateWithTimeIntervalSince1970
I used dateWithTimeIntervalSinceReferenceDate when getting the value.
NSTimeInterval is a typdef of a double
typedef double NSTimeInterval;
You can not store scalars directly in core data but you either have to wrap them in a NSNumber or in your case it may be easier to use a NSDate.
If startTime is a NSTimeInterval (and not an NSDate), you are comparing two different things there, a double and an NSDAte object.
[managedObject valueForKey:#"startTime"] will return you an NSTimeInterval, a primitive (which you should print with %f by the way).
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] will return you a NSDate.
If you really want to comare the two, you should use [NSDate dateWithTimeIntervalSince1970:[managedObject valueForKey:#"startTime"]] to properly compare two NSDate objects.
I thought I was doing the right thing here but I get several warnings from the Build and Analyze so now I'm not so sure. My assumption is (a) that an object I get from a function (dateFromComponents: in this case) is already set for autorelease and (b) that what I return from a function should be set for autorelease. Therefore I don't need to send autorelease or retain to the result of the dateFromComponents: before I return it to the caller. Is that right?
As a side note, if I rename my function from newTimeFromDate: to gnuTimeFromDate the analyzer does not give any warnings on this function. Is it the convention that all "new*" methods return a retained rather than autoreleased object?
In Memory Management Programming Guide for Cocoa it says "A received object is normally guaranteed to remain valid within the method it was received" and that "That method may also safely return the object to its invoker." Which leads me to believe my code is correct.
However, in Memory Management in Cocoa it says "Assume that objects obtained by any other method have a retain count of 1 and reside in the autorelease pool. If you want to keep it beyond the current scope of execution, then you must retain it." Which leads me to think I need to do a retain before returning the NSDate object.
I'm developing with Xcode 3.2.1 on 10.6.2 targeting the iPhone SDK 3.1.2.
screenshot of build/analyze output http://nextsprinter.mggm.net/Screen%20shot%202009-11-15%20at%2008.33.00.png
Here's the code in case you have trouble reading the screen shot:
//============================================================================
// Given a date/time, returns NSDate for the specified time on that same day
//============================================================================
+(NSDate*) newTimeFromDate:(NSDate*)fromDate
Hour:(NSInteger)hour
Minute:(NSInteger)min
Second:(NSInteger)sec
{
NSCalendar* curCalendar = [NSCalendar currentCalendar];
const unsigned units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* comps = [curCalendar components:units fromDate:fromDate];
[comps setHour: hour];
[comps setMinute: min];
[comps setSecond: sec];
return [curCalendar dateFromComponents:comps];
}
You are nearly right. The only problem that clang correctly points out is that your method promises a retain count +1 object (for its name containing “new”) but you are returning an autoreleased object.
You have two options: removing the “new" from the method name or retaining the returned object. It’s far more Cocoa-ish to return the autoreleased object (as you do) and name the method timeFromDate:.