9

Example: The -save: method of NSManagedObjectContext is declared like this:

- (BOOL)save:(NSError **)error

Since NSError is already a class, and passing a pointer would actually have the effect of modifying this object inside the implementation of -save:, what's the point of passing a pointer to a pointer here? What's the advantage/sense?

Usage example:

NSError *error;
if (![managedObjectContext save:&error]) {
    // Handle the error.
}
John Topley
  • 113,588
  • 46
  • 195
  • 237
openfrog
  • 40,201
  • 65
  • 225
  • 373
  • 3
    you should initialize error to nil in that example – ergosys Jan 14 '10 at 21:13
  • 7
    No, there is absolutely no need to initialize the error to nil. The value of the error is entirely undefined upon return from the method **unless** the method returned nil or NO. – bbum Jan 14 '10 at 22:14
  • 1
    I had always initialized NSErrors to nil, but I guess I was wrong in my interpretation of how errors were handled internally: http://rentzsch.tumblr.com/post/260201639/nserror-is-hard – Brad Larson Jan 14 '10 at 23:07
  • 2
    There is no harm in doing so. Only harm in expected it to matter. :) – bbum Jan 14 '10 at 23:40
  • Apple does never do that ;-) so I thought thats fine. – openfrog Jan 15 '10 at 11:12
  • @BradLarson I have confusion about ownership of NSErrors. When we pass a pointer to an NSError pointer as a parameter to method call, the NSError *e = [NSError alloc] must be happening in the called method and we get this reference in the caller after the called method is finished executing. Why don't we release the NSError in the caller and if it is autoreleased in the called method, then isn't it incorrect to play with that in the caller method because the lifecycle of the NSError was restricted to the called method. – SayeedHussain Aug 21 '13 at 09:03
  • I think we get around the autorelease issue because the drain of the thread's autorelease pool will not happen until the event loop has finished and goes idle. Is it correct? – SayeedHussain Aug 21 '13 at 09:17
  • @paranoidcoder - That sounds like a question to ask on its own, if it hasn't already been asked here. – Brad Larson Aug 21 '13 at 14:31

6 Answers6

16

If you just passed in a pointer, all the method could do would alter the already existing NSError object that you are pointing to.

By passing in a pointer to a pointer, it can create new NSError objects and leave you with a pointer that points to them.

Matt Greer
  • 60,826
  • 17
  • 123
  • 123
  • 5
    Sort of. If you passed in a reference to an existing NSError, the NSError implementation would have to support mutability. That would be an entirely different API contract. Otherwise, correct. – bbum Jan 14 '10 at 22:15
  • I added a relevant set of examples on this question. http://stackoverflow.com/questions/16244597/nserror-returned-with-bad-address-why – bbum Mar 06 '16 at 03:37
13

It is what some people refer to as an "out" parameter.

You're not passing a pointer to an NSError object, you're passing a pointer to a local variable. This gives the called method the ability to modify your local variable; in this case, to assign it to an NSError instance.

Perhaps what's confusing is that the local variable you're passing to save: is itself a pointer, so the variable type ends up being a pointer to a pointer.

Bottom line, it's a pointer to a local variable, and it works the same whether the local variable is an int or an NSError*.

Darren
  • 25,520
  • 5
  • 61
  • 71
  • I like your explanation but I'm uneducated in how a nil local variable has a valid address you can pass to another method. Can you please elaborate on that piece – Logicsaurus Rex May 23 '17 at 17:09
7

@Anon is correct. I'll add: This is the Cocoa way to produce errors, in place of throwing exceptions.

In your example, you have:

NSError *error = nil;
if (![managedObjectContext save:&error]) {
    // Handle the error.
}

Immediately after the call to save:, if there was an error, then the save: method will have created a new NSError object, and changed your error variable to point from nil to the new error object. That way you can examine the NSError object yourself and respond appropriately to it.

IMO, this is cleaner than throwing an exception (which in my philosophy should only be done when something catastrophic and unrecoverable happens).

Dave DeLong
  • 242,470
  • 58
  • 448
  • 498
4

It allows the method to allocate a new NSError and change the pointer to point to it, rather than having to modify the NSError already pointed-to (what if it's not big enough?)

Anon.
  • 58,739
  • 8
  • 81
  • 86
  • I'm confused. If you're sending in the address to a nil NSError, there is no NSError to 'modify' until you create one. Also, NSError is immutable, so you can't edit it anyway according to MacMark above. I wish I could make sense of all these conflicting answers. – Logicsaurus Rex May 23 '17 at 17:01
  • "what if it's not big enough?" - You are the first person I've found from a lot of sources to provide an example of why it may be a problem to modify the existing NSError / give a reason for its immutability. Thank you. – Ben Stoller Nov 03 '22 at 01:06
  • @LogicsaurusRex I'm sure you've resolved your misunderstanding by now but for anyone who might see this later; I think the first part of his answer was referring to how NSError ** implementations work, and the second part was a hypothetical implementation given a mutable NSError *. – Ben Stoller Nov 03 '22 at 01:12
3

The advantage is that you don't have to create the NSError object. As the documentation states:

"A pointer to an NSError object. You do not need to create an NSError object."

Peter B
  • 25,670
  • 4
  • 22
  • 18
  • Can you provide more clarification on this, with an example maybe, because MacMark above says `NSError` is immutable, so your only choice is to create one. I'm guessing you mean the CALLER does not need to create an `NSError`, however, if an error is going to be returned, then certainly the CALLED does need to create an `NSError` Your answer is a little unclear. – Logicsaurus Rex May 23 '17 at 16:59
2

If you just passed in a pointer, all the method could do would alter the already existing NSError object that you are pointing to.

You cannot alter an NSError object.

NSError is immutable. That's the reason you need the pointer to the NSError variable. You can only create a brand new NSError. Thus you change the pointer to point to your newly created NSError.

MacMark
  • 6,239
  • 2
  • 36
  • 41
  • True! this was the VERY reason why ** (pointer-of-pointer) was needed in order to retrieve NEW error encountered from the function caller. – J-Q Oct 19 '18 at 11:15