I'm developing an API using Objective-C, this API has protocol with some fictional method:
- (NSString *)gimmeString; // Want implementations to never return nil
I'm a big fan of providing context, so I heavily use everything for that purpose including attributes like __attribute__((nonnull)) and friends. What I'm asking is if there a way to provide context and possibly add a compile time check for method implementation saying "this method never returns nil" when compiled with clang?
i.e. I'd love to have something like:
#protocol MyProtocol
- (NSString *)gimmeString __attribute__((no_I_never_really_really_return_that_weird_nil));
#end
#implementation MyProtocolAdopter
- (NSString *)gimmeString
{
return nil; // WARNING! You're returning nil, YOU PROMISED!
}
#end
instead of just:
#protocol MyProtocol
// This method should never return nil
- (NSString *)gimmeString;
#end
#implementation MyProtocolAdopter
- (NSString *)gimmeString
{
// muvahaha, I lied!
return nil;
}
#end
I understand it is impossible to fully determine that at compile time, but detecting return nil; or functions which for sure evaluate to nil is fine.
Idea with something like __attribute__((objc_method_family(copy))) seems weird and unacceptable, but I didn't manage to find anything better then just adding a comment leaving my API users in a bit scarier and more unreliable world.
- XCode 6.3 -
Since XCode 6.3 you, actually, can annotate pointers to be nullable and nonnull and get compile time checks.
- (nonnull NSString *)gimmeString;
- Pre XCode 6.3 -
After some research, which included digging into clang and gcc docs I found that there's no way to achieve what I want in Objective-C.
Thoughts why is that so:
I believe that's because there's no way to determine that at compile time at some sophisticated level of quality. You can't tell for sure if method always returns not-nil at compile time. And determining if method could return nil is something shaky, guess, it's possible to evaluate all
return %something_which_evaluates_to_nil_at_compile_time%;
and warn on all methods which depend on that, but, for example, you can't be sure that some -init method does not always return nil or some web request get's you a request without additional context provided everywhere and you'll end up with false-negatives.
Even -dequeueReusableCellWithIdentifier:forIndexPath: from UITableView which guaranties to always return valid cell is defined as just:
- (id)dequeueReusableCellWithIdentifier:(NSString *)identifier
forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);
What I've came up with:
So, what I've came up in Objective-C is providing context without compile-time checks. I add comments like that:
#protocol MyProtocol
/**
* Creates and returns a string.
*
* #note Implementations should never return nil.
*
* #return A string, guarantied not to be nil.
*/
- (NSString *)gimmeString;
#end
Also one more thing could be done to make this more readable, we can define an empty define and append it to the end of declaration, like this:
// Marks method to never return nil
#define MD_RETURNS_NONNULL
#protocol MyProtocol
/**
* Creates and returns a string.
*
* #note Implementations should never return nil.
*
* #return A string, guarantied not to be nil.
*/
- (NSString *)gimmeString MD_RETURNS_NONNULL;
#end
So eye catches this line making your code more readable, making your code users happier because they understand what you want to emphasise easier.
Swift
And the last but not the least is suggestion to move to Swift. In Swift this is possible and built in, you just define your method to return not an optional type:
protocol MyProtocol {
func gimmeString -> String
}
And you're good to go.
Conclusion
If you really want checks like this in Objective-C there's no magic, you only do it at the run time, at the same time you could provide context, which is great. But oh, wait, there's also Swift option, we'll anyway migrate to it at some time, so take your time and spend some of it learning this great new language. This need is a great example where Swift's safety features are really suitable.
UPD: Played with OCLint a bit, looks like something like this could be achieved using it (or just writing a custom Clang extension).
Related
Say I have some Swift class with methods I'd like to expose to Objective-C:
#objc(Worker) class Worker : NSObject {
func performWork(label: String = "Regular Work") {
// Perform work
}
}
I can call this in two ways:
Worker().performWork()
Worker().performWork("Swift Development")
However, when called from Objective-C, I do not get the "default" version of this method - I must supply a label:
[[[Worker alloc] init] performWork:#"Obj-C Development"];
See the definition from my AppName-Swift.h header below:
SWIFT_CLASS_NAMED("Worker")
#interface Worker : NSObject
- (void)performWork:(NSString * __nonnull)label;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
I would expect to see a method with this signature:
- (void)performWork;
I realise ObjC doesn't support default parameter values, but my assumption was that the Swift default values simply generate additional Swift functions with trivial implementations calling the "real" function (i.e. they're syntactic sugar hiding the typical implementation we'd use in Obj-C).
Is there any way to expose this default value to Objective-C? If not, can anyone point me to any documentation on why this isn't available?
The answer is no, there is currently no way to expose that.
It is however, being considered for Swift 3.0 (see the proposal here)
EDIT: This is incorrect as pointed out by Adam S.
This is a question on how to gracefully circumvent the nullability of init in NSObject class.
So here is a classic objective-c implementation:
+ (instancetype)sharedInstance
{
static dispatch_once_t onceToken;
static id sharedInstance;
dispatch_once(&onceToken, ^
{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
But now I want to declare it as nonnull, if possible:
+ (nonnull instancetype)sharedInstance;
Unfortunately, init returns a nullable instancetype value. Should I add an NSAssert or something after calling init?
I noticed that some people even document nonnull values as being in reality nullable. Does that make sense?
Should I go bold and simply add NS_ASSUME_NONNULL_BEGIN everywhere, without truly ensuring values are nonnull?
There seem to be two questions here:
How do I ensure that the value produced within my singleton sharedInstance method and returned by that method is actually not nil at run time?
How do I satisfy the system of nullabilty annotations, compiler warnings, and Swift bridging that I'm returning a nonnull pointer?
Ensuring / enforcing nonnull
At some point, every API contract breaks down to a human contract. The compiler can help ensure that, for example, you can't take the result of a nullable call and return it from a method whose return type is nonnull... but somewhere there's usually an original call whose return type is nonnull simply because the programmer who wrote it said, "I promise to never return null, cross my heart, etc."
If you're familiar with Swift, this is similar to the situation with Implicitly Unwrapped Optionals — you use these when you "know" that a value cannot be nil, but can't prove that knowledge to the compiler because that knowledge is external to the source code (something from a storyboard or bundle resource, for example).
That's the situation here — you "know" that init will never return nil, either because you wrote / have source for the initializer in question or because it's just NSObject's init which is documented to return self without doing anything. The only situation where a call to that initializer will fail is because the preceding alloc call failed (and hence you're calling a method on nil, which always returns nil). If alloc is returning nil, you're already in dire straits and your process is not long for this world — this isn't a failure case to be designing API around.
(Nullability annotations are in general for describing intended use of an API, not the more extreme edge and corner cases. If an API call fails only because of a universal error it's not meaningful to annotate it as nullable; likewise, if an API fails only on input that can be ruled out via nonnull parameter annotations, the return value can be assumed nonnull.)
So, long story short: yes, just put NS_ASSUME_NONNULL around your header, and ship your sharedInstance implementation as is.
Returning a nonnull nullable
It's not the case here, but suppose you have a value that's annotated as nullable, but you know (or "know") that it can never be nil and want to return it from your nonnull-annotated method. And you're in a situation where you get a compiler warning for trying.
There's a syntax for that — just cast the value to the expected return type, annotations and all:
return (NSWhatever *_Nonnull)whatever;
In your case this shouldn't be needed — because you're dealing with the id and instancetype special types the compiler is more forgiving about nullability conversion and probably won't warn to begin with.
I am looking at a code base which is full of
NSString *const kTabChart = #"Charts";
NSString *const kTabNews = #"News";
and then
setSelectedTab:(NSString *)title;
...
someThingElse:(NSString *)title;
So these weak-typed NSString go far and all the way around the code and this just irritates my eyes. Enums would be better to some extent, but enums would not have the names available programmatically and I do not want to define all unrelated tab names from different views within the same enum {}
I wonder if there is a better way? I am dreaming of a way to make it something like
#interface PageTitle:NSSting;
PageTitle kTabChart = /some kind of initializer with #"Chart"/;
PageTitle kTabNews = /some kind of initializer with #"News"/;
I suspect that this would not play well with the whole "not a compile-time constant" constraint, but I wonder if there are tricks/patters/hacks to define constants of my own class type.
You should use NS_TYPED_ENUM as this makes it into a rawVlaue. More info https://developer.apple.com/documentation/swift/objective-c_and_c_code_customization/grouping_related_objective-c_constants
Sure, just think subclassing. First our class, which is a subclass of NSString:
#interface StringConstants : NSString
extern StringConstants * const kOptionApple;
extern StringConstants * const kOptionBlackberry;
#end
So we've defined StringConstants and a couple of global constants for it. To implement the class without any warnings just requires some casting:
#implementation StringConstants
StringConstants * const kOptionApple = (StringConstants *)#"Apple";
StringConstants * const kOptionBlackberry = (StringConstants *)#"Blackberry";
#end
And there is our set of constants. Let's test it:
- (void) printMe:(StringConstants *)string
{
NSLog(#"string: %#", string);
}
- (void) test
{
[self printMe:kOptionApple]; // Code completion offers the constants
[self printMe:#"Rhubarb"]; // Warning: Incompatible pointer types
[self printMe:(StringConstants *)#"Custard"]; // OK
}
You only get a warning, the code will run, as with other similar type errors.
You can of course repeat the pattern and produce one "class" per set of strings.
HTH
Addendum: It Is Safe (Trust Me For Now) and the Weak Enum
Concern has been raised in the comments that the above code is essentially dangerous, it is not. However in the general case the concerns raised are valid, it is safe here by design.
Note: This is being typed directly into SO. Please forgive the inevitable spelling & grammar errors, and the probable lack of good presentation, well defined explanatory arc, missing bits, redundant bits etc.
First let's add the missing comments to the above code, start with the implementation:
// The following *downcasts* strings to be StringConstants, code that
// does this should only appear in this implementation file. Use in
// other circumstances would effectively increase the number of "enum"
// values in the set, which rather defeats the purpose of this class!
//
// In general downcasting should only be performed after type checks to
// make sure it is safe. In this particular case *by design* it is safe.
StringConstants * const kOptionApple = (StringConstants *)#"Apple";
There are two different issues here
Is it safe at all - yes by design, trust me (for now); and
The addition of additional "enum" values
The second is covered by the second missing comment in the test code:
[self printMe:(StringConstants *)#"Custard"]; // OK :-( - go ahead, graft
// in a new value and shoot
// yourself in the foot if
// you must ;-)
The weak enum
Dealing with the second issue first, unsurprisingly this "enum" isn't bulletproof - you can add additional values on the fly. Why unsurprising? Because you can do it in (Objective-)C as well, not only is the language not strongly typed the enum types are the weakest of the lot. Consider:
typedef enum { kApple, kBlackberry } PieOptions;
How many valid values of PieOptions are there? Using Xcode/Clang: 2^32, not 2. The following is perfectly valid:
PieOptions po = (PieOptions)42;
Now while you should not write such obviously wrong code the need to convert between integers and enum values is common - e.g. when storing "enum" values in the tag field of UI controls - and thus the room for errors. C-style enumerations have to be used with discipline, and used that way are a good aid to program correctness and readability.
In the same way StringConstants must be used with discipline, no casting arbitrary strings - the equivalent of the 42 example above - and with discipline they have similar advantages and disadvantages to standard enumerations.
With the simple discipline of not casting arbitrary strings to StringConstants; something which is allowed only in the StringConstants implementation itself; this type gives you a completely safe "enumeration of string values" with compile-time warnings if used incorrectly.
And if you trust me you can stop reading now...
Addendum: Digging Deeper (Just Curious or We Don't Trust You)
To understand why StringConstants is completely safe (even adding additional values isn't really unsafe, though it may of course cause a program logic failure) we'll go through a number of issues about the nature of object-oriented programming, dynamic typing and Objective-C. SOme of what follows isn't strictly necessary to understand why StringConstants is safe, but you're someone with an inquiring mind aren't you?
Object reference casts don't do anything at runtime
A cast from one object reference type to another is a compile-time statement that the reference should be treated as being to an object of the destination type. It has no effect on the actual referenced object at runtime - that object has an actual type and it doesn't change. In the object-oriented model upcasting, going from a class to one of its superclasses, is always safe, downcasting, going in the reverse direction, may not (not is not) be safe. For this reason downcasting should be protected by tests in the cases it may be unsafe. For example given:
NSArray *one = #[ #{ #"this": #"is", #"a" : #"dictionary" } ];
The code:
NSUInteger len = [one.firstObject length]; // error, meant count, but NO compiler help at all -> runtime error
will fail at runtime. The result type of firstObject is id, which means any object type, and the compiler will let you call any method on references typed as id. The mistakes here are to not check the array bounds and that the retrieved reference is in fact a dictionary. A more bulletproof approach is:
if (one.count > 0)
{
id first = one.firstObject;
if ([first isKindOfClass:[NSDictionary class]])
{
NSDictionary *firstDict = first; // *downcast* to improve compile time checking
NSLog(#"The count of the first item is %lu", firstDict.count);
}
else
NSLog(#"The first item is not a dictionary");
}
else
NSLog(#"The array his empty");
The (invisible) cast is perfectly safe, protected as it is by the isKindOf: test. Accidentally type firstDict.length in the above code fragment and you will get a compile-time error.
However you only need to do this if the downcast might be invalid, if it cannot be invalid no tests are required.
Why can you call any method on references types as id?
This is where Objective-C's dynamic runtime message lookup comes into play. The compiler checks, to the best of its ability, for type errors at compile-time. Then at runtime another check is made - does the referenced object support the called method? If it does not a runtime error is generated - as occurs with the length example above. When an object reference is typed as id this is an instruction to the compiler to perform no compile-time check at all and leave it all to the runtime checks.
The runtime check is not checking the type of the referenced object, but instead whether it supports the requested method, which leads us to...
Ducks, NSProxy, Inheritance, et. al.
Ducks?!
In dynamic typing there is a saying:
If it looks like a duck, swims like a duck, and quacks like a duck, then it is a duck.
In Objective-C terms this means that at runtime if a referenced object supports the set of methods of type A then it is effectively an object of type A regardless of what it real type is.
This feature is used in many places in Objective-C, a notable example is NSProxy:
NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.
With NSProxy you may think you have, say, an NSDictionary - something which "looks, swims and quacks" like an dictionary - but in fact you've not got one at all. The important points are:
It does not matter; and
It is perfectly safe (modulo coding errors if course)
You can view this ability to substitute one object for another as a generalisation of inheritance - with the later you can always use a subclass instance in lieu of a superclass one, with the former you can use any object in place of another as long as it "looks, swims and quacks" like the object it is standing in for.
We've actually gone further than we need, ducks are not really required to understand StringConstants, so let's get on:
When is a string an instance of NSString?
Probably never...
NSString is implemented by a class cluster - a collection of classes which all respond to the same set of methods that NSString does, i.e. they all quack like NSString. Now these classes may be subclasses of NSString, but in Objective-C there is no actual need for them to be.
Furthermore if you think you have an instance of NSString you may actually have an instance of NSProxy... But it does not matter. (Well it might impact performance, but it doesn't impact safety or correctness.)
StringConstants is a subclass of NSString, so it certainly is an NSString, except that NSString instances probably don't exist - every string is actually an instance of some other class from the cluster, which may or may not itself be a subclass of NSString. But it does not matter!
As long as instances of StringConstants quack like NSStrings should then they are NSStrings - and all the instances we have defined in the implementation do that as they are strings (of some type, probably __NSCFConstantString).
Which leaves us with the question is the defining of the StringConstants constants sound? Which is the same question as:
When is Downcasting Known To Be Always Safe?
First an example of when it is not:
If you have a referenced typed as NSDictionary * then it is not known to be safe to cast it to NSMutableDictionary * without first testing whether the reference is to an mutable dictionary.
The compiler will always let you do the cast, you can then at compile-time make calls to mutating methods without error, but at run-time there will be errors. In this case you must test before casting.
Note that the standard test, isKindOf:, is effectively conservative due to all those ducks. You may actually have a reference to an object which quacks like an NSMutableDictionary but is not an instance of it - so the cast would be safe but the test will fail.
What makes this cast unsafe in general?
Simple, it is not known whether the reference object responds to the methods that an NSMutableDictionary does...
Ergo, if you did know that the reference must respond to all the methods of the type you are casting to then the cast is always safe and no test is required.
So how do you know the reference must respond to all the methods of the target type?
Well one case is straightforward: If you have a reference typed as T you can it to a reference of type S safely without any checks whatsever if:
S is a subclass of T - so it quacks like a T; and
S adds no instance state (variables) to T; and
S adds no instance behaviour (new methods, overrides etc.) to T; and
S overrides no class behaviour
S may add class new class methods (not overrides) and global variables/constants without violating these requirements.
In other words S is defined as:
#interface S : T
// zero or more new class methods
// zero or more global variables or constants
#end
#implementation S
// implementation of any added class methods, etc.
#end
And We Made It...
Or did we, anybody still reading?
StringConstants is by design constructed so that string instances can be cast to it. This should only be done in the implementation, sneaking in additional "enum" constants elsewhere goes against the purpose of this class.
It is safe, indeed its not even scary :-)
No actual instances of StringConstants are ever created, each constant is an instance of some string class safely at compile-time masquerading as a StringConstants instance.
It provides compile-time checking that a string constant is from a pre-determined set of values, it is effectively a "string valued enumeration".
Yet Another Addendum: Enforcing Discipline
You cannot completely automatically enforce the discipline required to code safely in Objective-C.
In particular you cannot have the compiler prevent programmers from casting arbitrary integer values into enum types. Indeed, due to uses such as the tag fields of UI controls, such casts need to be allowed in certain circumstances - they cannot be totally outlawed.
In the case of StringConstants we cannot have the compiler prevent the cast from a string everywhere except in the implementation of the class itself, just as with enum extra "enum" literals can be grafted in. This rule requires discipline.
However if discipline is lacking the compiler can assist with preventing all the ways, other than casting, that can be used to create NSString values and hence StringConstant values as it is a subclass. In other words all the initX, stringX etc. variations can be marked as unusable on StringConstant. This is done by simply listing them in the #interface and adding NS_UNAVAILABLE
You don't need to do this, and the answer above does not, but if you need this assistance to your discipline you can add the declarations below - this list was produced by simply copying from NSString.h and a quick search & replace.
+ (instancetype) new NS_UNAVAILABLE;
+ (instancetype) alloc NS_UNAVAILABLE;
+ (instancetype) allocWithZone:(NSZone *)zone NS_UNAVAILABLE;
- (instancetype) init NS_UNAVAILABLE;
- (instancetype) copy NS_UNAVAILABLE;
- (instancetype) copyWithZone:(NSZone *)zone NS_UNAVAILABLE;
- (instancetype)initWithCharactersNoCopy:(unichar *)characters length:(NSUInteger)length freeWhenDone:(BOOL)freeBuffer NS_UNAVAILABLE;
- (instancetype)initWithCharacters:(const unichar *)characters length:(NSUInteger)length NS_UNAVAILABLE;
- (instancetype)initWithUTF8String:(const char *)nullTerminatedCString NS_UNAVAILABLE;
- (instancetype)initWithString:(NSString *)aString NS_UNAVAILABLE;
- (instancetype)initWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) NS_UNAVAILABLE;
- (instancetype)initWithFormat:(NSString *)format arguments:(va_list)argList NS_FORMAT_FUNCTION(1,0) NS_UNAVAILABLE;
- (instancetype)initWithFormat:(NSString *)format locale:(id)locale, ... NS_FORMAT_FUNCTION(1,3) NS_UNAVAILABLE;
- (instancetype)initWithFormat:(NSString *)format locale:(id)locale arguments:(va_list)argList NS_FORMAT_FUNCTION(1,0) NS_UNAVAILABLE;
- (instancetype)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding NS_UNAVAILABLE;
- (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)len encoding:(NSStringEncoding)encoding NS_UNAVAILABLE;
- (instancetype)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)len encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)freeBuffer NS_UNAVAILABLE;
+ (instancetype)string NS_UNAVAILABLE;
+ (instancetype)stringWithString:(NSString *)string NS_UNAVAILABLE;
+ (instancetype)stringWithCharacters:(const unichar *)characters length:(NSUInteger)length NS_UNAVAILABLE;
+ (instancetype)stringWithUTF8String:(const char *)nullTerminatedCString NS_UNAVAILABLE;
+ (instancetype)stringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) NS_UNAVAILABLE;
+ (instancetype)localizedStringWithFormat:(NSString *)format, ... NS_FORMAT_FUNCTION(1,2) NS_UNAVAILABLE;
- (instancetype)initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding NS_UNAVAILABLE;
+ (instancetype)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc NS_UNAVAILABLE;
- (instancetype)initWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error NS_UNAVAILABLE;
- (instancetype)initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error NS_UNAVAILABLE;
+ (instancetype)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error NS_UNAVAILABLE;
+ (instancetype)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error NS_UNAVAILABLE;
- (instancetype)initWithContentsOfURL:(NSURL *)url usedEncoding:(NSStringEncoding *)enc error:(NSError **)error NS_UNAVAILABLE;
- (instancetype)initWithContentsOfFile:(NSString *)path usedEncoding:(NSStringEncoding *)enc error:(NSError **)error NS_UNAVAILABLE;
+ (instancetype)stringWithContentsOfURL:(NSURL *)url usedEncoding:(NSStringEncoding *)enc error:(NSError **)error NS_UNAVAILABLE;
+ (instancetype)stringWithContentsOfFile:(NSString *)path usedEncoding:(NSStringEncoding *)enc error:(NSError **)error NS_UNAVAILABLE;
Have you thought about the #define macro?
#define kTabChart #"Charts"
During the pre-processing step of compilation, the compiler will swap out all kTabChart w/ your desired constant.
If you want constants of your own custom class, then you will have to use const as user #JeremyP says in the linked answer
What did I do here? Can method parameters be typeless?
In a recent project which works fine, the App is in the store already - no issues so far, I did the following in a .h file:
#property (strong, nonatomic) NSManagedObject *myPerson;
- (HKPersonPicker*) initWithPerson:person;
- (HKPersonPicker*) initWithPerson:person delegate:(id <HKPersonPickerDelegate>)delegate;
I meant to do it this way but did not:
- (HKPersonPicker*) initWithPerson:(NSManagedObject*)person;
- (HKPersonPicker*) initWithPerson:(NSManagedObject*)person delegate:(id <HKPersonPickerDelegate>)delegate;
The corresponding part of the .m file:
- (HKPersonPicker*) initWithPerson:person
{
self = [super init];
if(self){
self.myPerson = person;
}
return (self);
}
- (HKPersonPicker*) initWithPerson:person delegate:(id <HKPersonPickerDelegate>)delegate
{
self = [self initWithPerson:person];
if(self){
self.delegate = delegate;
}
return (self);
}
As you can see I did not do anything special with this person object, just assigned it to the property myPerson. As I did not call any of the object's methods or accessed its data, the compiler did not need to know anything about that object. But what is it from the compiler's point of ivew? Type id? Just something? Is there any default type?
As you see, I do not have any real problem. I am just curious. Hope this question does not break any SO rule.
With C the default type is int, but with Objective-C it's id.
Missing type declarations in methods default to id. IIRC, you can see a fair number of methods without return types in Apple's runtime library.
There is no definitive problem with what you are doing, but its not really kosher at the same time. You should still have a type declaration for the sake of clarity, and maintaining good and consistent Cocoa-like code styling.
So it should really be like:
- (GSPersonPicker*) initWithPerson:(id)person;
And you should really change the property declaration:
#property (strong, nonatomic) id *myPerson;
If that is not changed, then your code is confusing. You KNOW the type that will be coming in. So let the compiler help you with meaningful warnings/errors by using types. You should also probably be using the type 'HKPerson' or whatever you have named your entity, so the compiler doesn't think its ok for you to pass in an 'HKPlace' (they will both be of type 'id' and 'NSManagedObject', which does you no favors)
More importantly, you should not be using the dynamic type (id) unless you have a reason. The compiler is there to help you. Errors and warnings are your friend, they tell you that you messed up, not the computer. When you use 'id', the compiler just goes, Oh, anything can go in here!!! And it will not detect an error where you sent that method a type that will break everything.
Now lets say that you are going to use polymorphism. IE, Lets declare a generic protocol for multiple classes to adhere to, which defines our person:
#protocol GSPerson
-(NSUInteger)age;
#end
So now lets define a couple classes, and have them subscribe to the protocol (the GSPerson thats between carrots markdown is killing me atm lol):
#import "GSPerson.h"
#interface GSSpecialPerson <GSPerson>
// code
#end
#import "GSPerson.h"
#interface GSWeirdPerson <GSPerson>
// code
#end
Then lets redefine our method signature to adhere to the protocol:
- (GSPersonPicker*) initWithPerson:(id<GSPerson>)person;
And our property declaration:
#property (strong, nonatomic) id <GSPerson> *myPerson;
Now the compiler knows that anything i pass into that method should conform to the GSPerson protocol I defined above (ie they need a method that returns the persons age). If i try to pass anything else in, it will throw compiler warnings, WHICH IS GOOD.
Even better, it will throw warnings on your polymorphic classes if they are missing their required methods.
I've many times seen a case where a programmer needs to assign some value (Object or primitive type, does not matter). And let's say this value is an NSString and can be obtained from the following expression
[myObject description]// returns NSString
But for some reason I've seen many people declare another method that itself returns an NSString and executes the above command only. Like:
-(NSString*)getDescription{
return [myObject description];
}
Is this just a matter of preference, or is is there some benefit from it?
Is this just a matter of preference, or is is there some benefit from it?
Those one line wrappers are often used to:
introduce behavior of a method that is meant to be overridden
or (more frequently) to simplify the program. If the method did not exist, you may find the complexity of the program grows. It serves to demonstrate intent, for clarity, documentation, and to minimize redundant implementations (simplifying the program).
There is definitely some "benefit" of creating a method or even better, overriding the "standard" NSObject description method..
If you have a custom NSObject for example and override the +(NSString *)description method you can then return information directly inside that object.
Take for example the following was overwritten in the NSObject we called foo.
+ (NSString *)description {
return #"Hello there";
}
Now, if you ever called [foo description] it would return the string "Hello there".
However, if you just returned description without overwriting the description method, it'd return something like <foo>0x12234 or something.
So yeah, it definitely has a lot of benefit to overriding a custom NSObject description.