2

I'm working on an iPhone app using objective C. I've got class A, which creates an NSMutableArray pointer called "list". But, in class A, I never create an object for it to point to. Instead, I call a class method in class B, to get some data from an sqlite database, and populate an NSMutableArray object there. I want to be able to set the pointer in class A to point to the NSMutableArray object created in the class B method by passing it as an argument to the method. I can't do it by returning the array because I want to return the sqlite result.

I wonder if I'm doing this right? I haven't written the entire method yet (when it's done it'll be pretty long), but I want to know if I'm doing the pointer stuff correctly before I get started on the rest.

//ClassA.m

//...
NSMutableArray *list;
[ClassB populateArrayFromSqlite:&list];
//Now do stuff with "list", which should point to the array created in populateArrayFromSqlite
//...

//ClassB.m
+(int)populateArrayFromSqlite:(NSMutableArray**)ar {
    NSMutableArray *array = [[NSMutableArray alloc] init];
    //Here there will be code that populates the array using sqlite (once I write it)
    //And then I need to set the "list" pointer in ClassA.m to point to this array
    ar = &array; //Is this how?

    return resultFromSqlite;
}

Did I do it right? Or am I not understanding something? I guess this pointer-to-pointer stuff just doesn't click with me yet. After reading a few general sources about pointers, I suspect this is how I'd do it but part of me doesn't understand why the ar argument can't just be a regular pointer (as opposed to a pointer-to-pointer).

Matt
  • 22,721
  • 17
  • 71
  • 112
Adam
  • 125
  • 4

2 Answers2

6

Pointers to pointers are a bit escheric, yes. The simple way to do it would be to create an empty array in A and pass a regular array pointer to B that would just fill it. If you insisted on creating the array in B, I think you could do this:

- (void) createArray: (NSMutableArray**) newArray
{
    NSAssert(newArray, @"I need a pointer, sweetheart.");
    *newArray = [[NSMutableArray alloc] init];
    [*newArray addObject:...];
}
zoul
  • 102,279
  • 44
  • 260
  • 354
  • 1
    +1 though it'd be best to check `if (newArray != nil)`, just so you don't accidentally dereference a null pointer. :) – Dave DeLong Jan 08 '11 at 19:59
  • Right. I mentioned this possibility just to answer the pointer-to-pointer question, in practice I’d certainly use the simpler solution I mentioned first. I’ll add an assert to make things clear. – zoul Jan 08 '11 at 20:01
  • Thanks, I didn't even think of creating the object first in class A and passing a pointer to it to the class B method. Also, with your example I think I understand this. Passing the address of a pointer in the newArray argument actually sends a copy of a pointer to the pointer, and by dereferencing it, you get the pointer itself, which you can then assign a new address to, correct? – Adam Jan 08 '11 at 20:15
  • Yes, that’s how I understand it, too. – zoul Jan 08 '11 at 20:30
  • Goodness. Don't **do** that! Reaching into another class in such a fashion to set an ivar is an egregious abuse of encapsulation and should *never* be done in a properly written/architected Objective-C application! – bbum Jan 09 '11 at 00:46
  • Alright, I decided to "return" my sqlite error code using a pointer-to-pointer and instead returning the array normally. So now I'm just using list = [ClassB createAndPopulateArray]; Thanks. – Adam Jan 09 '11 at 02:08
2

Changing pointers to objects like this is fairly uncommon in Objective-C. The main time we see stuff like this is when a method has a potential for failure, at which point we'll pass in a pointer to an NSError reference. For example, NSFileManager's - (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error.

The far more common way to do this would be to have the method return the array:

NSMutableArray * list = [ClassB mutableArrayFromSQLite];
Dave DeLong
  • 242,470
  • 58
  • 448
  • 498