e.g.
NSError *error = nil;
if ([something error:&error]) {
// ...
}
// ...
if ([somethingElse error:&error]) {
// ...
}
Will I run into any problems if I use the same NSError without resetting it to nil?
Ordinarily I'd say it doesn't matter, but I worry that someone else would use one error when they meant to use the other.
No problems, but to do that check error before passing the same pointer as argument. If the something method has an error and the somethingElse too, the error variable will have a pointer to the second one. Think you pass a pointer to a pointer to an object.
NSError *error = nil;
if ([something error:&error]) {
// ...
}
if (error) {
//...
}
if ([somethingElse error:&error]) {
// ...
}
I don't think you will have problems with it. Your NSError variable will be rassigned only.
Related
I have writing a function in AppDelegate.m, which is :
-(void)saveContext {
NSError *error = nil;
if (![self.managedObjectContext save:&error]) {
if (error) {
NSLog(#"Unable to save changes.");
NSLog(#"%#, %#", error, error.localizedDescription);
}
}
}
However, i received an error message :
Receiver type 'NSManagedObjectContext' for instance message is a forward declaration
My question is : How I could fix it ?
Thanks in advance
I think I'm missing something pretty fundamental about programming in objective-c.
The callstack is as follows:
MyViewController calls a block to setup a request to my server.
[_myClient storePhoto:photo withCompletion: ^(KNPhotoInfo *retPhoto, NSError *error) { // do stuff }];
This call sets up a request to my server to save the photo and then tries to handle the response:
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if(error) //handle it
#try
{
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
KNPhotoInfo *photoInfo = [[KNPhotoInfo alloc] initWithPhotoDictionary:json[#"data"]];
[_storage setObject:[photoInfo toDictionary] forKey:PhotoInfoKey];
completionHandler(photoInfo, nil);
}
#catch (NSException *exception)
{
NSLog(#"Error parsing user info: %#", exception);
}
}];
However if I inspect the photoInfo variable before its init function is called I can see some data (not just garbage) in it. If I step into the init function, when something is assigned I can see the self object flash but then it just says "0 objects" again. After the call to the init funciton has returned, everything seems fine, I can see my object with all it's properties initialized with json["data"], but as soon as I step photoInfo says "0 objects" again.
Can anyone help me out here? I think this has something to do with "self" of the calling class vs self of where the objected is initialized, but I'm confused. :s
EDIT: To add a little more context. The init function looks something like this:
- (instancetype) initWithPhotoDictionary:(NSDictionary *)dict{
self = [super init];
self.url = dict[#"photo"][#"url"];
self.challenge = dict[#"photo"][#"challenge"];
self.user = dict[#"photo"][#"user"];
self.pubDate = dict[#"photo"][#"pub_date"];
return self;
}
However when debugging inside of this call, self has no children (even though it obviously does) and just says "0 objects". What's frustrating about this is when the call to
[_storage setObject:[photoInfo toDictionary] forKey:PhotoInfoKey];
happens I'm getting complaints about null values. However if I "print description" of photoInfo I can see all of my fields, filled out correctly.
use this methode
- (KNPhotoInfo) initWithPhotoDictionary:(NSDictionary *)dict
{
KNPhotoInfo *photoInfo = [[KNPhotoInfo alloc] init
photoinfo.url = dict[#"photo"][#"url"];
photoinfo.challenge = dict[#"photo"][#"challenge"];
photoinfo.user = dict[#"photo"][#"user"];
photoinfo.pubDate = dict[#"photo"][#"pub_date"];
return photoInfo;
}
I'm trying to call a method from swift.
The method is in a singleton written in objective-C
the block in the header file:
typedef void(^VPersonResultBlock)(Person *person, NSError *error);
- (void)askForMe:(VPersonResultBlock)block;
and here's the implementation of that method.
- (void)askForMe:(VPersonResultBlock)block
{
if (_me) block(_me,nil);
else {
[Person getMeWithBlock:^(PFObject *person, NSError *error) {
if (!error) {
_me = (Person *)person;
block(_me,nil);
}
else if (error) {
block(nil,error);
}
else {
NSDictionary *userInfo = #{
NSLocalizedDescriptionKey: NSLocalizedString(#"Operation was unsuccessful.", nil),
NSLocalizedFailureReasonErrorKey: NSLocalizedString(#"The operation failed to retrieve the user.", nil),
NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(#"Check your network connection and try again", nil)
};
NSError *error = [[NSError alloc] initWithDomain:#"VisesAsyncErrorDomain" code:-10 userInfo:userInfo];
block(nil,error);
}
}];
}
}
In Objective-C, I can call this and it autocompletes without confusion.
[[VDataStore instance] askForMe:^(Person *person, NSError *error) {
// do things with myself that aren't strange
}];
Now let's say I want to call the same method from swift. The bridging header is setup, with the header file imported, but swift's expectation is confusing.
VDataStore.askForMe(VDataStore)
This is what shows up in the autocomplete options
(VPersonResultBlock!) -> Void askForMe(self: VDataStore)
what I was hoping for, was for this to autocomplete into a closure, and although it appears to see all of the information correctly, what it's expecting isn't lining up with what objective-C seems to understand.
How do I call this properly from swift?
Directly translate your ObjC calling code to Swift is
VDataStore.instance().askForMe() {
person, error in
// do things with myself that aren't strange
}
Your problem is that askForMe is instance method but you are accessing from class object VDataStore.askForMe. Swift will give you a function object that takes an instance as input.
Its a common pattern to add an error output parameter when writing Objective-c methods.
As far as I know this is how you create a method that return an error if something is wrong:
- (void)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
Now there are times when you just want that error parameter to reflect an error occurred in some other method you use inside your method, lets say:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:nil];
}
So I thought ok, I'll just pass the error parameter to the inside method, like this:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (error) {
NSLog(#"error %#", error);
}
}
But this approach has two issues:
1. the if (error) check returns YES even if there is no error.
2. the log line generates this warning: Format specifies type 'id' but the argument has type 'NSError *__autoreleasing *'
So what am I doing wrong here?
There are a couple of things wrong. Firstly the NSError object should not be used to test for errors, instead use the method's return value. Therefore your first example method should return BOOL to indicate success:
- (BOOL)doSomethingWithObj:(id)obj error:(NSError *__autoreleasing *)error {
BOOL success = NO;
// do somthing...
if (!success) {
if (error) { // Check it's been passed, and if so create the error object.
*error = [NSError errorWithDomain:#"the.domain" code:0 userInfo:nil];
}
}
return success;
}
And test for results being nil, not error being non-nil:
- (void)fetchObjectInContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"SomeObject"];
NSArray *results = [context executeFetchRequest:request error:error];
if (!results) {
if (error && *error)
NSLog(#"error %#", [(*error) localizedDescription]); // Here is your 2. I think.
else
NSLog(#"Unknown error");
}
}
Secondly the error parameter is commonly optional (as seen in your code where you pass nil, which should be NULL actually). Therefore you need to test if it's been passed before dereferencing it (see code above).
However to answer your overall question, yes it's good to pass the error parameter along to subordinate method calls and is commonly used.
I have no idea about your 2. until you update your code... standing by. I think your 2. issue is because you need to use [error localizedDescription] with NSLog().
You are passing address of error not actual error this means &error
So you need to derefrence the error pointer.NSError *__autoreleasing * you are taking parameter as address of error.We generally do this because objective c can return only one value.But error need to be known from where we are calling the mehod so passing it as address of error will make change to error if there an error comes in calle function.
So if any error comes in below line
NSArray *results = [context executeFetchRequest:request error:error];
than it is automatically know to calle function i.e doSomethingWithObj
if (*error) {
NSLog(#"error %#", (*error).description);
}
Use
NSLog(#"error %#", (*error).description);
instead of
NSLog(#"error %#", (error).description);
you have to pass &error
At the moment I can't see any AUser objects in my sqlite3 app database.
This is the code I have to create a user. Am I missing something? There are no warnings/errors with the code.
AUser *user = [NSEntityDescription insertNewObjectForEntityForName:#"AUser" inManagedObjectContext:_managedObjectContext]; // _managedObjectContext is declared elsewhere
user.name = username; //username is some string declared elsewhere / name is an attribute of AUser
You need to perform a save on the context.
NSError* error = nil;
if(![context save:&error]) {
// something went wrong
NSLog(#"Error saving context: %#\n%#", [error localizedDescription], [error userInfo]);
abort();
}
When you perform a save, data in the context are saved to the persistent store coordinator (hence in your sql store).
P.S. Based on the discussion with #G. Shearer, in production if the context fails to save, you can handle the error gracefully. This means not using abort() call that causes the app to crash.
NSError* error = nil;
if(![context save:&error]) {
// save failed, perform an action like alerting the user, etc.
} else {
// save success
}
You need to call save after creating the object.
Example:
NSError *error;
if ([user.managedObjectContext save:&error]) {
//Success!
} else {
//Failure. Check error.
}
you need to call
NSError *error = nil;
[_managedObjectContext save:&error];
if(error != nil) NSLog(#"core data error: %#",[[error userInfo] description]);
before the object will be persisted to the database