Question

I have an NSArray and I need to change the order of the items within it. I have written a method that will determine the new order:

+(NSArray*)sortProxyForms:(NSArray*)arrayOfForms
{
    NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {

        return [@(a.ordinal) compare:@(b.ordinal)]; // @(a.ordinal) aka Boxing turns int into NSNumber

    }];

    arrayOfForms = [sortedForms copy]; // DOES NOT WORK

    return sortedOfForms; // WORKS IF ASSIGNED IN THE CALLER

}

So, I can pass the NSArray to be sorted into the method. I call the method like this:

[HPSModelUtilities sortProxyForms:_formProxies];

If I actually try setting arrayOfForms (a reference to _formProxies) within the method then once I have returned from the method then the array is unchanged.

However, if I return the sorted array from the method and assign it to the NSArray in the calling method then the assignment works:

_formProxies = [HPSModelUtilities sortProxyForms:_formProxies]; // _formProxies NSArray is changed

_formProxies is declared in the calling class, and "HPSModelUtilities" is a different class.

How come the NSArray can be changed in the caller, but not changed in the called method, even though it is passed by reference?

Was it helpful?

Solution

When you pass a value into a method it is copied. This is called "pass by value". The arrayOfForms you are passing in is a pointer to an NSArray. This means that the pointer is copied when passed in. Redirecting this pointer to another instance of an NSArray does not change where the original pointer is pointing.

I would rename your method to (NSArray*)sortedArrayFromProxyForms:(NSArray*)proxyForms

If you really want to change where your NSArray reference is pointing in the method. Do it like this.

+ (void)sortProxyForms:(NSArray**)proxyForms {
    *proxyForms = sortedForms;
}

OTHER TIPS

You are passing a copy of the array reference (subtly different than passing by reference), but then you are changing where that reference points with this line:

arrayOfForms = [sortedForms copy];

arrayOfForms no longer points to the array instance you passed, but to a different array. You could pass a pointer of pointer, and change where the caller's pointer is pointing, but for what you are doing, I think the reassignment is fine.

If you'd really like here's what your function would look like with pointer of pointer:

+(void)sortProxyForms:(NSArray**)arrayOfForms {
    NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {

        return [@(a.ordinal) compare:@(b.ordinal)]; // @(a.ordinal) aka Boxing turns int into NSNumber

    }];

    *arrayOfForms = [sortedForms copy]; 
}

but I'll add the caveat that this isn't a pattern you see often in objective-c, so I'd avoid it when there are other alternatives available.

Also note when calling this function you need to add the & to get the extra level of indirection:

[HPSModelUtilities sortProxyForms:&_formProxies];
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top