Just trying to figure out what's the best practice: when using method that takes (NSError**), is it better to send it nil or NULL?
For example,
NSArray *items = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:NULL];
In the documentation it says "You may specify nil for this parameter if you do not want the error information." On the other hand, since its a double pointer, NULL seems to make sense as well?
Technically, NULL is the right answer and the docs are wrong.
In practice, it matters not. NULL and nil are the same, for all intents and purposes.
While that could change and remain language compliant, it couldn't change without breaking tons and tons of stuff.
Feel free to file a bug, though.
Related
I am creating an application on iPhone where I am taking code written and Java and translating into Objective-C, as the app must work similarly in both Android and iPhone. Now problems of course are there are certain areas where the languages differ quite a bit, and can be difficult to implement just right.
One of the areas where it is of course different is you cannot create NSMutableArrays or NSMutableDictionaries which only accepts objects of a certain type. So when I am creating the getters and setters for these objects, I have been doing this, example below:
-(void)setPerson:(NSMutableArray *)personList_p
{
BOOL bAllowed = YES;
for(int i = 0; i < [personList_p count]; i++)
{
if(![[personList_p objectAtIndex:i] isKindOfClass:[Person class]])
{
bAllowed = NO;
break;
}
else
{
bAllowed = YES;
}
}
if(bAllowed)
{
personList_i = personList_p;
}
else
{
//Raise error here
}
}
Now originally I was using NSRaise Exception, but after doing reading up on it, it recommends against this as exception handling is resource intensive and should not be used for this type of situation. So NSError I read was a good solution, so I was reading up on this guide
http://www.cimgf.com/2008/04/04/cocoa-tutorial-using-nserror-to-great-effect/
My question is two fold, if I decide to use NSError should, is it best practice to declare the NSError object in the method, i.e like this
-(void)setPerson:(NSMutableArray *)personList_p : (NSError **)error
Or is it okay just to declare the error within the else statement above, like this
NSError *error = nil;
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"Invalid object passed into Array, object must be of type Person." forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:#"Invalid Object" code:200 userInfo:details];
NSLog(#"%#", [error localizedDescription]);
Secondly, in the guide I linked above, in the conclusion the author says
In this example, I am checking to see if the error is still nil after
my message call. If it is no longer nil I know that an error occurred
and that I need to display it to the user. Apple provides a built in
method to do this with a call to presentError: on the NSApplication
instance
and he uses this line of code here
[NSApp presentError:error];
This does not work for me, I do not get an autocomplete option with this code, just says use of undeclared identifier. Am I missing something obvious here?
Also, just to be sure, is the method I am using, the best method to go about ensuring the user sends in an array with the correct object types? Or is there another method which could be used which would achieve the same thing but in a more efficient manner.
Thanks in advance!!
EDIT:
Additional question, would it be such a bad idea to use NSException, as the problem with NSError is the app carries on, I am considering having it so if they do pass in an array with an invalid object, that it does just cause the application to crash and raise an exception. Is there any major reason why I shouldn't do this??
Now originally I was using NSRaise Exception, but after doing reading
up on it, it recommends against this as exception handling is resource
intensive and should not be used for this type of situation. So
NSError I read was a good solution, so I was reading up on this guide
He does not actually say that raising exceptions is inherently bad or resource intensive, he argues that #try/catch is the wrong way to go about things. That, and Cocoa applications (even more so I'm Cocoa-Touch) should refrain from throwing exceptions whenever possible. Exceptions indicate undefined behavior, not simple errors.
Secondly, in the guide I linked above, in the conclusion the author...
uses this line of code here
[NSApp presentError:error];
This does not work for me, I do not get an autocomplete option with
this code, just says use of undeclared identifier. Am I missing
something obvious here?
That is because NSApp is a concept only accessible to Cocoa(or Mac) applications. It represents a pointer to "the application itself", and has some pretty neat functions associated with it. Of course, iOS has no parallel concept, so errors are traditionally presented in a UIAlertView.
Additional question, would it be such a bad idea to use NSException,
as the problem with NSError is the app carries on, I am considering
having it so if they do pass in an array with an invalid object, that
it does just cause the application to crash and raise an exception. Is
there any major reason why I shouldn't do this??
It is generally a bad idea to use NSException for anything other than extremely serious logic (such as when a call is made to an index out of the bounds of an NSArray, or when a UITableView's data source happens to be out of sync with what the table demands). If you absolutely must stop program execution, it's much cleaner to use NSAssert() to throw conditional exceptions. And even then, setters are not the place to be throwing exceptions. It would be far easier to simply return nil when the condition fails, then have the caller check for such an outcome and handle it appropriately.
I have an NSString (which is a path to a file) in my code that I would like to somehow obfuscate or encrypt,
but still be able to call up the file path easily when needed.
I searched for an answer to this, but everything I've seen either deals specifically with iOS or seems overly complicated.
I would simply like to use it with something such as this:
- (void)method {
NSString *obfuscate = #"/path/to/something/secret"; // encrypt or obfuscate
[self manageFiles:obfuscate]
- (void)manageFiles(NSString *)obfuscate {
NSFileManager *files = [[NSFileManager alloc] init];
if ([files fileExistsAtPath:obfuscate])
... .
— any help is appreciated, thank you.
(This is an old question, but I'm replying anyway)
There's no such way to in Obj-C. Obj-C is dynamic enough that any of these methods can be trapped and intercepted. Do not ship anything in a application that absolutely needs to be secret. If your application is run on a jailbroken phone, or if it is made available on piracy sites, than it has already been exposed and it's memory contents dumped. All these above methods copy the decoded data to main memory where it is exposed.
See:
https://www.youtube.com/watch?v=Ii-02vhsdVk
None of these methods above is actually secure. Again, do not embed these sorts of things in your applications with an assurance they are actually secure.
What I have done in the past to obfuscate a string was something to this extent:
-(NSString*)myString {
NSString *string = nil;
string = [#"ozzzzzzzzzzzzhazzzzzzzizzzzzz" stringByReplacingOccurrencesOfString:#"z" withString:#""];
return string;
}
What it would do is remove all the occurences of the letter z, leaving you with ohai as a string. Not sure if this will suffice for your case, but it has worked for me.
Hope this helps!
I am just starting climbing the Objective C learning curve (using Nerd Ranch iOS programming book).
Based on what I have know from other languages about "nesting" multiple executions within one line I assumed that I can alter:
NSString* descriptionString = [[NSString alloc] initWithFormat:#"%#", possesionName]
with a two line version:
NSString* descriptionString = [NSString alloc];
[descriptionString initWithFormat:#"%#", possesionName]
but it seems that the second attempt raises an exception
2012-01-22 18:25:09.753 RandomPossessions[4183:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -length only defined for abstract class. Define -[NSPlaceholderString length]!'
Could someone help me understand what exactly I am doing wrong here? Thanks a lot in advance.
PS. If this is a way Objective C messages work and you have to make alloc and init in one line just let me know - I assumed this is just a set of functions that either can be executed two in one go or one after another.
An important difference between both versions (they are not exactly equal) is that in the first version you use the result of initWithFormat for the variable descriptionString, while you use the result of alloc in the second. If you change your code to
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName]
all should be well again. It is specified that an object returned by alloc shall not be seen as initialized and functional until some init Method has been called and init might return something else.
The alloc method will allocate memory for a new object. But the init method might throw away that memory and return a completely different object. Or it might return nil. This is why you must always do self = [super init] when you override an init method.
NSString is one class that does this kind of thing all the time.
I'm not exactly sure why the exception is happening, but I believe it could be ARC injecting code in between your two lines of code or something similar. Whatever it is, something is trying to act on the allocated object that has never been initialised, and this is a huge problem that can lead to all kinds of issues. Consider yourself lucky it threw an exception, sometimes it wont.
The NSString class might not actually be a real class. It may contain almost no methods and almost no variables. All it has is a bunch of factory methods to create "real" string objects of some other class, and this is done using methods like initWithFormat:. So, by long standing convention alloc/init must always be done in a single statement and there are a handful of places where, usually for performance reasons, something will rely on this convention being used.
Basically, objective-c is a language where you don't need to know exactly what is going on inside an object. You just need to know what messages can be sent to an object, and how it will respond. Anything else is undefined behaviour and even if you learn how it works, it is subject to change without notice. Sometimes the behaviour will change depending on circumstances that are completely illogical, for example you might expect the "copy" method to give you a copy of the object you send it to, and while this is the default behaviour, there are many cases where it will actually just return the same object with slightly different memory management flags. This is because the internal logic of the class knows that returning the same object is much faster and effectively identical to returning an actual copy.
My understanding is copy sent to NSString may return a new object, or it may return itself. It depends on which NSString subclass is actually being used, and there isn't even any documentation for what subclasses exist, let alone how they're implemented. All you need to know, is that copy will return a pointer to an object that is perfectly safe to treat as if it was a copy even though it might not be.
In a "proper" object oriented language like Objective-C, objects are "black boxes" which can intelligently change their internal behaviour at any time for any reason, but their external behaviour always remains the same.
With regard to avoiding nesting... The coding style for Objective-C often does require extensive nesting, or else you'll be writing 10 lines of code when only 1 is really needed. The square brace syntax is particularly suited to nesting without making your code messy.
As a rule of thumb, I turn on Xcode's "Page Guide at column" feature, and set it to 120 characters. If the line of code exceeds that width then I'll think about breaking it into multiple lines. But often it's cleaner to have a really long line than three short lines.
Be pragmatic about it. :)
From Apple's library reference, initWithFormat:
Returns an NSString object initialized by converting given data into Unicode characters using a given encoding.
So you can use these two lines of code:
NSString* descriptionString = [NSString alloc];
descriptionString = [descriptionString initWithFormat:#"%#", possesionName];
For more info please go to:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html#//apple_ref/occ/instm/NSString/initWithFormat:
I have recently read Apple's sample code for MVCNetworking written by Apple's Developer Technical Support guru Quinn "The Eskimo!". The sample is really nice learning experience with what I guess are best development practices for iOS development.
What surprised me, coming from JVM languages, are extremely frequent assertions like this:
syncDate = [NSDate date];
assert(syncDate != nil);
and this:
photosToRemove = [NSMutableSet setWithArray:knownPhotos];
assert(photosToRemove != nil);
and this:
photoIDToKnownPhotos = [NSMutableDictionary dictionary];
assert(photoIDToKnownPhotos != nil);
Is that really necessary? Is that coding style worth emulating?
If you're used to Java, this may seem strange. You'd expect an object creation message to throw an exception when it fails, rather than return nil. However, while Objective-C on Mac OS X has support for exception handling; it's an optional feature that can be turned on/off with a compiler flag. The standard libraries are written so they can be used without exception handling turned on: hence messages often return nil to indicate errors, and sometimes require you to also pass a pointer to an NSError* variable. (This is for Mac development, I'm not sure whether you can even turn exception handling support on for iOS, considering you also can't turn on garbage collection for iOS.)
The section "Handling Initialization Failure" in the document "The Objective-C Programming Language" explains how Objective-C programmers are expected to deal with errors in object initialization/creation: that is, return nil.
Something like [NSData dataWithContentsOfFile: path] may definitely return nil: the documentation for the method explicitly says so. But I'm honestly not sure whether something like [NSMutableArray arrayWithCapacity: n] ever returns nil. The only situation I can think of when it might is when the application is out of memory. But in that case I'd expect the application to be aborted by the attempt to allocate more memory. I have not checked this though, and it may very well be that it returns nil in this case. While in Objective-C you can often safely send messages to nil, this could then still lead to undesirable results. For example, your application may try to make an NSMutableArray, get nil instead, and then happily continue sending addObject: to nil and write out an empty file to disk rather than one with elements of the array as intended. So in some cases it's better to check explicitly whether the result of a message was nil. Whether doing it at every object creation is necessary, like the programmer you're quoting is doing, I'm not sure. Better safe than sorry perhaps?
Edit: I'd like to add that while checking that object creation succeeded can sometimes be a good idea, asserting it may not be the best idea. You'd want this to be also checked in the release version of your application, not just in the debug version. Otherwise it kind of defeats the point of checking it, since you don't want the application end user to, for example, wind up with empty files because [NSMutableArray arrayWithCapacity: n] returned nil and the application continued sending messages to the nil return value. Assertions (with assert or NSAssert) can be removed from the release version with compiler flags; Xcode doesn't seem to include these flags by default in the "Release" configuration though. But if you'd want to use these flags to remove some other assertions, you'd also be removing all your "object creation succeeded" checks.
Edit: Upon further reflection, it seems more plausible than I first thought that [NSMutableArray arrayWithCapacity: n] would return nil rather than abort the application when not enough memory is available. Basic C malloc also doesn't abort but returns a NULL pointer when not enough memory is available. But I haven't yet found any clear mention of this in the Objective-C documentation on alloc and similar methods.
Edit: Above I said I wasn't sure checking for nil is necessary at every object creation. But it shouldn't be. This is exactly why Objective-C allows sending messages to nil, which then return nil (or 0 or something similar, depending on the message definition): this way, nil can propagate through your code somewhat similar to an exception so that you don't have to explicitly check for nil at every single message that might return it. But it's a good idea to check for it at points where you don't want it to propagate, like when writing files, interacting with the user and so on, or in cases where the result of sending a message to nil is undefined (as explained in the documentation on sending messages to nil). I'd be inclined to say this is like the "poor man's" version of exception propagation&handling, though not everyone may agree that the latter is better; but nil doesn't tell you anything about why an error occurred and you can easily forget to check for it where such checks are necessary.
Yup. I think it's a good idea.. It helps to filter out the edge cases (out of memory, input variables empty/nil) as soon as the variables are introduced. Although I am not sure the impact on speed because of the overhead!
I guess it's a matter of personal choice. Usually asserts are used for debugging purpose so that the app crashes at the assert points if the conditions are not met. You'd normally like to strip them out on your app releases though.
I personally am too lazy to place asserts around every block of code as you have shown. I think it's close to being a bit too paranoid. Asserts might be pretty handy in case of conditions where some uncertainity is involved.
I have also asked this on Apple DevForums. According to Quinn "The Eskimo!" (author of the MVCNetworking sample in question) it is a matter of coding style and his personal preference:
I use lots of asserts because I hate debugging. (...)
Keep in mind that I grew up with traditional Mac OS, where a single rogue pointer could bring down your entire machine (similarly to kernel programming on current systems). In that world it was important to find your bugs sooner rather than later. And lots of asserts help you do that.
Also, even today I spend much of my life dealing with network programs. Debugging network programs is hard because of the asynchrony involved. Asserts help to with this, because they are continually checking the state of your program as it runs.
However, I think you have a valid point with stuff like +[NSDate date]. The chances of that returning nil are low. The assert is there purely from habit. But I think the costs of this habit (some extra typing, learning to ignore the asserts) are small compared to the benefits.
From this I gather that asserting that every object creation succeeded is not strictly necessary.
Asserts can be valuable to document the pre-conditions in methods, during development, as design aid for other maintainers (including the future self). I personally prefer the alternative style - to separate the specification and implementation using TDD/BDD practices.
Asserts can be used to double-check runtime types of method arguments due to the dynamic nature of Objective C:
assert([response isKindOfClass:[NSHTTPURLResponse class]]);
I'm sure there are more good uses of assertions. All Things In Moderation...
There is a built-in shortcut NSApp, is there any reason that I shouldn't add one for NSFileManager?
#define NSFM [NSFileManager defaultManager]
I think omitted this will make my code cleaner and I can't see any benefit to keeping it in. (I plan on doing this in all my projects from now on, so it won't be obscure.)
NSFileManager *fm = [NSFileManager defaultManager]
Why don't you just use a local variable?
NSFileManager *fm = [NSFileManager defaultManager];
// use fm...
or better yet, inject the file manager as a method argument:
- (void)myMethod {
//using [NSFileManager defaultManager]
}
becomes
- (void)myMethodWithFileManager:(NSFileManager*)fm {
//usin fm
}
Since the defaultManager is a singleton (effectively a global), it makes testing really hard. Injecting the dependency saves you typing (as you want) within the method and makes it much easier to unit test—you can inject a test double instead of the defaultManager.
Finally, Cocoa and Objective-C generally come down on favoring explicit code over short code. The philosophy is basically that using more verbose names makes the code easier to read (and thus to maintain). This philosophy goes all the way to Objective-C's selector style with interleaved named arguments. Unless you really can't handle the extra typing (and Xcode's code completion won't work for you), stick with the existing names. When in Rome and all that...
If it makes your code cleaner, I'm all for it. Just keep in mind that any other developers who have to read your code will not immediately know what NSFM or fm represent.
I would suggest a slightly more descriptive name: NSFileMgr. To most Cocoa developers, this would make the purpose of the variable a lot clearer without having to look it up.
UPDATE: See Barry Wark's answer for some very good points in regards to testing.
I would use a local variable every time because using the defaultManager is not thread safe. If at any point you start using threads in your application you may end up with hard to find bugs and not know why until you run upon this bit of documentation.