I'm writing a game that includes a Quest class, which has a completionDate property of type NSDate *, which records the date and time that the player completed the quest. It's declaration (in Quest.h) is:
#property (nonatomic) NSDate *completionDate;
I figured it would be reasonable, in a case where the player had not yet completed a quest, to assign the quest's completionDate property a value of NULL to indicate this. However, when I do:
quest.completionDate = NULL;
XCode (11.1) gives a warning on that line of code: Null passed to a callee that requires a non-null argument.
Despite the warning, my code seems to work as intended: Doing a check like the following to determine whether or not the quest has been completed seems to work fine:
if (quest.completionDate == NULL) { ... }
Researching this, I found similar questions like Xcode 7, Obj-C, "Null passed to a callee that requires a non-null argument", with answers suggesting just using the init method to initialize an object to a default state. However, in the case of NSDate, init initializes the date to "now" (the current date/time), so that won't work for what I'm trying to do.
My question: Is there any reason not to assign NULL to an NSDate *?
Add the nullable property attribute to your completionDate.
#property (nonatomic, nullable) NSDate *completionDate;
To learn more about declaring nullable types, see this Apple blog post Nullability and Objective-C.
Related
This has me a little mystified.
For some reason I can set a date via a property, but trying to set it the same way using KVC causes an error.
I'm trying to make my code more generic, so I'd really like to be able to set my dates via KVC, given my NSManagedObject definitions as they are below.
This isn't my project, and it's a very large project, so I need to work within what's already there.
I read this, but it doesn't quite address this particular issue...
Why is an NSDate in a Core Data managed Object converted to NSTimeInterval?
In the core data modeler...
NSManagedObject SomeClass
updateDate Date
In code...
NSManagedObject SomeClass
#property (nonatomic) NSTimeInterval updateDate
So in a method, I've seen code that does this when setting through the property, which works just fine, but isn't generic.
someClass.updateDate = [NSDate date] timeIntervalSinceReferenceDate
I can't do that through KVC becaus setValue: expects an ID, so it fails with this compile time error 'Sending NSTimeInterval to parameter of incompatible type 'id_Nullable'' when the same is done through KVC
[someClass setValue:[NSDate date].timeIntervalSinceReferenceDate forKey:#"updateDate"];
Suggestions for fixing this so I can use KVC to set my dates.
Thanks a lot
If you pass an NSNumber to setValue:forKey:, it is automatically unboxed if the property is a primitive type. So you should be able to do [someClass setValue:#([[NSDate date] timeIntervalSince1970]) forKey:#"updateDate"].
In the header for NSLocale, currentLocale is declared like this:
+ (id /* NSLocale * */)currentLocale; // an object representing the user's current locale
It's obvious that they are returning id on purpose, but I'm curious why that would be necessary. Could this method ever return anything other than an NSLocale instance?
Back in the day, one used NSDictionary objects for locale information. See, for example, the "Special Considerations" documented for -[NSString compare:options:range:locale:]:
Special Considerations
Prior to OS X v10.5, the locale argument was an instance of NSDictionary. On OS X v10.5 and later, if you pass an instance of NSDictionary the current locale is used instead.
Some methods, such as -[NSDate dateWithNaturalLanguageString:locale:] still take an NSDictionary.
Other methods, such as many classes' -descriptionWithLocale:, can take either.
Anyway, with the introduction of NSLocale the types of various locale parameters was generalized to id to accommodate either kind of object without breaking source compatibility. The return type of +[NSLocale currentLocale] is similar generic so that it can be passed to methods that used to only take NSDictionary objects.
Initializers (even convenience initializer) traditionally return id. This prevents problems when you subclass. For instance, imagine this scenario:
#interface Foo : NSObject
- (Foo *)initWithBar:(Bar *)bar;
#end
#interface Baz : Foo
- (Baz *)initWithBar:(Bar *)bar;
#end
This would be a compiler error. You are redefining initWithBar: to return a different type. But if you always return Foo*, then Baz *baz = [Baz initWithBar:bar] would fail because initWithBar: returns a superclass.
To get yourself out of this problem, all initializers have historically returned id if there's any chance the class will be subclassed (which is to say, you should really always do this).
Recently, clang added instancetype, which solves this problem more elegantly by representing "the type of the current class." This is only useable in the interface. You can't declare variables to be of type instancetype (I've actually wanted this in some cases…) id is automatically promoted to instancetype for methods that begin with init…. Otherwise, you need to use it manually. Many older Cocoa interfaces haven't been updated yet, but they're slowly moving over to instancetype.
Sanity check. Given this:
#property (readwrite, nonatomic) NSDate *start;
Then aren't these 2 lines of code identical?
Version 1:
self.start.description
Version 2:
[self.start description]
i.e. start.description calls the description method on the start object.
Yes, they're identical, and so is [[self start] description]
Basically yes.
Around the property there is a setter and getter autosynchoronizsed. In the event that you use an #syncronize statement then you have a chance to influence how the setter, getter and property are named. If you autosynchronize the property's name is _start. The getter name is start and the setter name is setStart.
So
something = self.start
actually calls the getter and
self.start = something
calls the setter.
Equivalents are:
something = [self start];
[self setStart:something];
If you ever want to access the instance variable directly then do so by:
_start = something;
something = _start;
In the event that you just use #synthesize start; then the equivalent would be:
start = something;
something = start;
That may well be confusing but start actually addresses the instance variable while self.start uses the setter/getter. This difference comes to vast importance when you do not ARC. Depending on the property parameters (e.g. assign, copy, retain, ...) the automatically generated getter and setter does some memeory management for you while the memory management is left to you when you work directly with the instance variable.
Yes. The result will be identical in both cases; properties are (mostly) just sugar around accessor methods written in the conventional Cocoa style.
I say "mostly" because there are some minor internal differences. Properties are added as meta-data to the runtime description of the Objective C class. You can, via some reflection, find out a list of properties that have been declared as such. This list is different from a list of methods that are named in the style of getter/setters.
I have this in my interface:
#property (nonatomic, weak) NSTimeInterval *timeStamp;
Which my logic told me, I need a time stamp object, that only is going to be used by this class within the context of its instantiation, so "weak" seemed to be logical to me-- but XCode tells me "property with 'weak' attribute must be of object type"... If I just do:
#property (nonatomic) NSTimeInterval *timeStamp;
Then the error goes away, but I am not sure I understand why...
The problem is that NSTimeInterval is a value type -- it's an alias for double, essentially (check NSDate.h for the typedef). The weak attribute only applies to objects that have a retain count (that is, anything that descends from NSObject or NSProxy).
As such, storing a pointer to NSTimeInterval is probably a mistake on your part. You will most likely never receive a pointer to an NSTimeInterval unless you're expected to write to a given address as an output to a function (probably a callback in such a case). That said, I'm not aware of any functions with NSTimeInterval * as a return type nor any that pass the same to a callback.
Objective-C, xCode for iOS
In a class, I want to assign a singleton integer's value. Right now I have:
[ExGlobal sharedMySingleton].tetraCountEx = tetraCount;
I've got this warning before, and have been able to resolve it, but this seems like I would have to do something different by letting the compiler know that tetraCountEx is an integer. I just don't know how.
That error is a result of trying to store a number as a pointer. With out you posting any code as to how tetraCountEX is declared I can only guess what your problem is.
On reason may be that tetraCountEx is defined as an NSNumber and if that is the case use
[ExGlobal sharedMySingleton].tetraCountEx = [NSNumber numberWithInt:tetraCount];
//or numberWithInteger: or the appropriate type
And the other reason may be accidentally declaring tetraCountEx as a pointer
//Remove the * if this is the case
#property(nonatomic, assign) int *tetraCountEx;