I have a method that takes an (NSError **) but I don't want to pass it one. When I pass it "nil" or "NULL" I end up with "EXC_BAD_ACCESS".
The offending line is "*error = nil;"
How do I pass it the equivalent of "nil" without crashing?
The offending line should be:
if (error != nil) { *error = nil; }
You're trying to dereference a null pointer, which is a surefire way to crash.
The alternative would be to pass it an NSError**, but then just ignore it afterwards.
When you are implementing such method, you can use the following trick so that you won’t have to check for nil all the time:
- (void) doSomethingMaybeCausing: (NSError**) error
{
NSError *dummyError = nil;
if (error == NULL)
error = &dummyError;
// ...later:
*error = [NSError …];
}
Now the caller can pass NULL if he’s not interested in the errors.
You have to pass it a valid pointer to something which it can modify. The standard way to do this would be:
NSError *error = nil;
[object callMethod:whatever error:&error];
Related
I'm trying to test out using static libraries, and am calling this method (which is in the static library)
-(NSMutableDictionary *)parseJSONfromURL:(NSURL *)url{
__strong NSMutableDictionary *json;
[self.delegate isParsing:(url != nil)];
if (url == nil) {
[NSException raise:NSArgumentDomain format:#"The passed url argument cannot be nil"];
}
NSError *err;
json = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:url] options:NSJSONReadingMutableLeaves error:&err];
if (err) {
[json setObject:err forKey:#"error"];
}
while (json == nil) {
NSLog(#"waiting...");
}
[self.delegate isParsing:NO];
[self.delegate didFinishParsing:(json != nil)];
return json;
}
I would expect the while loop to be infinite since json is returning null, but the delegate method didFinishParsing gets sent, meaning it isn't null.
like this (ACParser is a class in the library)
ACParser *p = [[ACParser alloc] initWithDelegate:self];
dictionary = [p parseJSONfromURL:[NSURL URLWithString:#"http://www.a-cstudios.com/text.json"]];
dictionary is declared like this
__strong NSMutableDictionary *dictionary;
the JSON at that URL is very simple
{
"text" : "testing"
}
however, every time this is called, in the delegate method didFinishParsing:, logging dictionary returns (null). What am I doing wrong here? Is it because I'm calling it from a static library?
Try adding this:
NSLog(#"%#", [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url]] options:NSJSONReadingAllowFragments error:nil]);
in there and see if it prints out your expected JSON. If it doesnt, your URL is wrong. If it does, then your data isnt being retained. Try instantiating your json variable like this:
NSMutableDictionary *json = [NSMutableDictionary dictionaryWithDictionary:[NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:url] options:NSJSONReadingMutableLeaves error:&err]];
"json" within "parseJSONfromURL" is autoreleased / out of scope as soon as that method returns so it never has a chance to get assigned to your strong "dictionary" property.
That's why you are seeing NULL.
Try setting the property within your parseJSONfromURL method and see if that works, or create a non-autoreleased dictionary and return that.
I have an array which I want to validate: {"2","+","3","=","5"}
So how would I do it? I found DDMathParser and theoretically I would be able to validate my expression like so:
NSString *expression = [array componentsJoinedByString:#""];
NSError *error = nil;
DDExpression *e = [DDExpression expressionFromString:expression error:&error];
if (error == nil) {
NSLog(#"parsed: %#", e);
NSNumber * result = [e evaluateWithSubstitutions:expression error:&error];
//error: No visible #interface for 'DDExpression' declares the selector 'evaluateWithSubstitutions:error:'
}
Any thoughts why? I must mention this method is inside my singleton but when I moved it to my view controller error stayed.
Assuming you are using this project (GiHub link), I did not see a method evaluateWithSubstitutions:error:.
From the GitHub project, it looks like the full method signature is (source, line 33):
- (NSNumber *) evaluateWithSubstitutions:(NSDictionary *)substitutions evaluator:(DDMathEvaluator *)evaluator error:(NSError **)error;
-(NSData *) decryptData:(NSData *) Data
{
NSData *iv;
NSData *salt;
NSError *error;
NSData *stringData = Data;
NSInputStream *inpStream = [[NSInputStream alloc] initWithData:stringData];
bool IVData = [inpStream _CMgetData:&iv maxLength:kAlgorithmIVSize error:&error];
bool SaltData = [inpStream _CMgetData:&salt maxLength:kPBKDFSaltSize error:&error];
}
#implementation NSInputStream (Data)
-(BOOL)_CMgetData:(NSData **)data maxLength:(NSUInteger)maxLength error:(NSError **)error {
NSMutableData *buffer = [NSMutableData dataWithLength:maxLength];
if ([self read:buffer.mutableBytes maxLength:maxLength] < 0)
{
if (error)
{
*error = [self streamError];
NSLog(#"CMgetData Error!!");
return NO;
}
}
*data = buffer;
NSLog(#"%#", buffer);
return YES;
}
#end
I always get "CMgetData Error!!" Does anyone know why this issue comming.. I believe i passed all the parameters correctly. is that right?? Please help
Thanks in advance Any help??
Any stream must be opened before you can read from or write to it:
NSInputStream *inpStream = [[NSInputStream alloc] initWithData:stringData];
[inpStream open];
// now you can read from the stream.
In decrypt data you create a pointer to an NSError. This pointer gets an adress somewhere in memory and you pass that adress to _CMgetData. That adress will always evaluate to true, therefore (when read returns a negative number) you always enter the if(error) block.
You probably want to check whether your call to read:maxLength: had an error, as it returns with a negative number. Check the specs of that function how to retrieve error information.
For non-ARC code, local variables, particularly NSError instances that you are later checking for non-nil status, must be initialised to nil, otherwise they will hold garbage. Declare as:
NSError *error = nil;
Instead.
why is "error:&error" used here (objective-c)
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
wouldn't an object in objective-c be effectively pass-by-reference anyway?
The argument type for error: is NSError** (i.e. a pointer to a pointer to an object). This permits the moc object to allocate and initialize a new NSError object as required. It is a common pattern, especially in Cocoa.
The NSError documentation gives some indication of the motivation for this approach:
Applications may choose to create subclasses of NSError to provide better localized error strings by overriding localizedDescription.
Passing in an NSError** argument allows that method to return any subclass of NSError that makes sense. If you passed in NSError*, you would have to supply an existing NSError object, and there would be no way for the method to return a different object from the one you passed in.
To be clear, the method could look something like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError**)error {
...
if ((error != NULL) && (some_error_condition)) {
*error = [[[SomeNSErrorSubclass alloc] init...] autorelease];
return nil;
}
}
Note that this also allows the calling code to ignore errors by simply passing in NULL for the error: parameter, as follows:
NSArray *array = [moc executeFetchRequest:request error:NULL];
Update: (in response to questions):
There are two reasons why the argument type has to be NSError** instead of NSError*: 1. variable scoping rules, and 2. NSError instances are imutable.
Reason #1: variable scoping rules
Let's assume that the function declaration were to look like this:
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error;
And we were to call the function like this:
NSError * error = nil;
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
When you pass in a variable this way, the function body will not be able to modify the value of that variable (i.e. the function body will not be able to create a new variable to replace the existing one). For example, the following variable assignments will exist only in the local scope of the function. The calling code will still see error == nil.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error = [[[NSError alloc] init...] autorelease]; // local only
error = [[[SomeNSErrorSubclass alloc] init...] autorelease]; // local only
}
Reason #2: instances of NSError are immutable
Let's keep the same function declaration, but call the function like this:
NSError * error = [[[NSError alloc] init...] autorelease];
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }
First of all, the variable scoping rules guarantee that error can not be nil, so the if (error != nil) { ... condition will always be true, but even if you wanted to check for specific error information inside the if block, you would be out of luck because instances of NSError are immutable. This means that once they are created, you cannot modify their properties, so the function would not be able to change the domain or userInfo of that NSError instance that you created in the calling code.
- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
...
error.domain = ... // not allowed!
error.userInfo = ... // not allowed!
}
It's effectively another return value. The error is not dominant by convention in Cocoa when there is a return value for the operation. When an error is encountered, it may be returned to you by this out parameter.
In the case of NSError, it works this way because NSError is not a mutable type - its fields are set at initialization and never mutated. Therefore, you cannot pass an NSError as usual and set the error code.
I am working on catching errors in my app, and I am looking into using NSError. I am slightly confused about how to use it, and how to populate it.
Could someone provide an example on how I populate then use NSError?
Well, what I usually do is have my methods that could error-out at runtime take a reference to a NSError pointer. If something does indeed go wrong in that method, I can populate the NSError reference with error data and return nil from the method.
Example:
- (id) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
// begin feeding the world's children...
// it's all going well until....
if (ohNoImOutOfMonies) {
// sad, we can't solve world hunger, but we can let people know what went wrong!
// init dictionary to be used to populate error object
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"ran out of money" forKey:NSLocalizedDescriptionKey];
// populate the error object with the details
*error = [NSError errorWithDomain:#"world" code:200 userInfo:details];
// we couldn't feed the world's children...return nil..sniffle...sniffle
return nil;
}
// wohoo! We fed the world's children. The world is now in lots of debt. But who cares?
return YES;
}
We can then use the method like this. Don't even bother to inspect the error object unless the method returns nil:
// initialize NSError object
NSError* error = nil;
// try to feed the world
id yayOrNay = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!yayOrNay) {
// inspect error
NSLog(#"%#", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.
We were able to access the error's localizedDescription because we set a value for NSLocalizedDescriptionKey.
The best place for more information is Apple's documentation. It really is good.
There is also a nice, simple tutorial on Cocoa Is My Girlfriend.
I would like to add some more suggestions based on my most recent implementation. I've looked at some code from Apple and I think my code behaves in much the same way.
The posts above already explain how to create NSError objects and return them, so I won't bother with that part. I'll just try to suggest a good way to integrate errors (codes, messages) in your own app.
I recommend creating 1 header that will be an overview of all the errors of your domain (i.e. app, library, etc..). My current header looks like this:
FSError.h
FOUNDATION_EXPORT NSString *const FSMyAppErrorDomain;
enum {
FSUserNotLoggedInError = 1000,
FSUserLogoutFailedError,
FSProfileParsingFailedError,
FSProfileBadLoginError,
FSFNIDParsingFailedError,
};
FSError.m
#import "FSError.h"
NSString *const FSMyAppErrorDomain = #"com.felis.myapp";
Now when using the above values for errors, Apple will create some basic standard error message for your app. An error could be created like the following:
+ (FSProfileInfo *)profileInfoWithData:(NSData *)data error:(NSError **)error
{
FSProfileInfo *profileInfo = [[FSProfileInfo alloc] init];
if (profileInfo)
{
/* ... lots of parsing code here ... */
if (profileInfo.username == nil)
{
*error = [NSError errorWithDomain:FSMyAppErrorDomain code:FSProfileParsingFailedError userInfo:nil];
return nil;
}
}
return profileInfo;
}
The standard Apple-generated error message (error.localizedDescription) for the above code will look like the following:
Error Domain=com.felis.myapp Code=1002 "The operation couldn’t be completed. (com.felis.myapp error 1002.)"
The above is already quite helpful for a developer, since the message displays the domain where the error occured and the corresponding error code. End users will have no clue what error code 1002 means though, so now we need to implement some nice messages for each code.
For the error messages we have to keep localisation in mind (even if we don't implement localized messages right away). I've used the following approach in my current project:
1) create a strings file that will contain the errors. Strings files are easily localizable. The file could look like the following:
FSError.strings
"1000" = "User not logged in.";
"1001" = "Logout failed.";
"1002" = "Parser failed.";
"1003" = "Incorrect username or password.";
"1004" = "Failed to parse FNID."
2) Add macros to convert integer codes to localized error messages. I've used 2 macros in my Constants+Macros.h file. I always include this file in the prefix header (MyApp-Prefix.pch) for convenience.
Constants+Macros.h
// error handling ...
#define FS_ERROR_KEY(code) [NSString stringWithFormat:#"%d", code]
#define FS_ERROR_LOCALIZED_DESCRIPTION(code) NSLocalizedStringFromTable(FS_ERROR_KEY(code), #"FSError", nil)
3) Now it's easy to show a user friendly error message based on an error code. An example:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:FS_ERROR_LOCALIZED_DESCRIPTION(error.code)
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
Great answer Alex. One potential issue is the NULL dereference. Apple's reference on Creating and Returning NSError objects
...
[details setValue:#"ran out of money" forKey:NSLocalizedDescriptionKey];
if (error != NULL) {
// populate the error object with the details
*error = [NSError errorWithDomain:#"world" code:200 userInfo:details];
}
// we couldn't feed the world's children...return nil..sniffle...sniffle
return nil;
...
Objective-C
NSError *err = [NSError errorWithDomain:#"some_domain"
code:100
userInfo:#{
NSLocalizedDescriptionKey:#"Something went wrong"
}];
Swift 3
let error = NSError(domain: "some_domain",
code: 100,
userInfo: [NSLocalizedDescriptionKey: "Something went wrong"])
Please refer following tutorial
i hope it will helpful for you but prior you have to read documentation of NSError
This is very interesting link i found recently ErrorHandling
I'll try summarize the great answer by Alex and the jlmendezbonini's point, adding a modification that will make everything ARC compatible (so far it's not since ARC will complain since you should return id, which means "any object", but BOOL is not an object type).
- (BOOL) endWorldHunger:(id)largeAmountsOfMonies error:(NSError**)error {
// begin feeding the world's children...
// it's all going well until....
if (ohNoImOutOfMonies) {
// sad, we can't solve world hunger, but we can let people know what went wrong!
// init dictionary to be used to populate error object
NSMutableDictionary* details = [NSMutableDictionary dictionary];
[details setValue:#"ran out of money" forKey:NSLocalizedDescriptionKey];
// populate the error object with the details
if (error != NULL) {
// populate the error object with the details
*error = [NSError errorWithDomain:#"world" code:200 userInfo:details];
}
// we couldn't feed the world's children...return nil..sniffle...sniffle
return NO;
}
// wohoo! We fed the world's children. The world is now in lots of debt. But who cares?
return YES;
}
Now instead of checking for the return value of our method call, we check whether error is still nil. If it's not we have a problem.
// initialize NSError object
NSError* error = nil;
// try to feed the world
BOOL success = [self endWorldHunger:smallAmountsOfMonies error:&error];
if (!success) {
// inspect error
NSLog(#"%#", [error localizedDescription]);
}
// otherwise the world has been fed. Wow, your code must rock.
Another design pattern that I have seen involves using blocks, which is especially useful when a method is being run asynchronously.
Say we have the following error codes defined:
typedef NS_ENUM(NSInteger, MyErrorCodes) {
MyErrorCodesEmptyString = 500,
MyErrorCodesInvalidURL,
MyErrorCodesUnableToReachHost,
};
You would define your method that can raise an error like so:
- (void)getContentsOfURL:(NSString *)path success:(void(^)(NSString *html))success failure:(void(^)(NSError *error))failure {
if (path.length == 0) {
if (failure) {
failure([NSError errorWithDomain:#"com.example" code:MyErrorCodesEmptyString userInfo:nil]);
}
return;
}
NSString *htmlContents = #"";
// Exercise for the reader: get the contents at that URL or raise another error.
if (success) {
success(htmlContents);
}
}
And then when you call it, you don't need to worry about declaring the NSError object (code completion will do it for you), or checking the returning value. You can just supply two blocks: one that will get called when there is an exception, and one that gets called when it succeeds:
[self getContentsOfURL:#"http://google.com" success:^(NSString *html) {
NSLog(#"Contents: %#", html);
} failure:^(NSError *error) {
NSLog(#"Failed to get contents: %#", error);
if (error.code == MyErrorCodesEmptyString) { // make sure to check the domain too
NSLog(#"You must provide a non-empty string");
}
}];
extension NSError {
static func defaultError() -> NSError {
return NSError(domain: "com.app.error.domain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Something went wrong."])
}
}
which I can use NSError.defaultError() whenever I don't have valid error object.
let error = NSError.defaultError()
print(error.localizedDescription) //Something went wrong.
Well it's a little bit out of question scope but in case you don't have an option for NSError you can always display the Low level error:
NSLog(#"Error = %# ",[NSString stringWithUTF8String:strerror(errno)]);