2

I have an odd question (to me, anyway).

I am calling a function within my Objective-C program and am passing in three arguments... a home cooked object, an NSMutableArray, and an NSString.

The function prototype looks like this....

 int LoadNoteTableArray (Entryfile* EntryfileName, 
      NSMutableArray* NSMutableArrayInputEntry, NSString* title_string);

I call it from within main like this...

 Entryfile* Entryfile1 = [[Entryfile alloc] init];
 NSMutableArray *NoteTableArray = [[NSMutableArray alloc] initWithCapacity:1];
 NSString*          title_string;

title_string = @"test string";

checkerror = LoadNoteTableArray (Entryfile1, NoteTableArray, title_string);

And it is set up in the function like this....

 int LoadNoteTableArray (Entryfile* Entryfile1, NSMutableArray* NoteTableArray,
   NSString* title_string) {

I am able to use the object Entryfile1 and change it in the function using the objects various methods and those changes are seen in the main program. I am able to add entries into NoteTableArray and see then back in the main program. However, changes to the NSString do not get reflected back in the main program. I am able to see title_string as @"test string" at the start of the function. It is set to something else in the function but that value never makes it back to the main program.

I am confused at the difference between these notations and objects.

Thoughts?

Ok.... code snippers... from the main routine...

checkerror = LoadConfigTableArray (Configfile1, ConfigEntryArray);
title_string = @"test string";
checkerror = LoadNoteTableArray (Entryfile1, NoteTableArray, &title_string);
[NoteTableArray sortUsingSelector:@selector(compareNoteEntryTime:)];
checkerror = RemediateNoteTableArray (NoteTableArray);
checkerror = WriteOutTimeFromNoteTableArray (NoteTableArray, ConfigEntryArray,       title_string);
[NoteTableArray sortUsingSelector:@selector(compareNoteIDandMarkTime:)];
checkerror = WriteOutNotesFromNoteTableArray (NoteTableArray, ConfigEntryArray);

Please be aware that check error will be used to catch error conditions reported from the various functions after I get this problem worked out...

You asked for it... you got it... Toyota... here is the function... I'm not cleaning up the spacing for this posting site. Some of the code was play code so if you want to go after it that's ok but be aware that you're not helping me much. It reads a text file (Mac or Windows), parses certain lines into objects, and loads them into an object array.

int LoadNoteTableArray (Entryfile* Entryfile1, NSMutableArray* NoteTableArray, NSString** title_string) {

NSString* tokenclass;
NSString* previous_tokenclass;
NSString* token;
NSString* savedheaderid;
NSString* response;
NSString* tempstring;
NSString* tempstring2;
NSString* token_xx;
NSString* entryfile1path;
NSString* entryfile1name;
NSString* responseok;
NSNumber*   temp_MarkTime;

NoteTableEntry* NoteTableEntrytemp;

char firstline;
char first_id_found;
int duplicate_count;
int arrayCount;
int loop_count;

entryfile1path = [NSString stringWithString: @"PATH"];
entryfile1name = [NSString stringWithString: @"notes"];
responseok = [Entryfile1 OpenEntryFile: entryfile1path withdatafilename: entryfile1name];
if ([responseok isEqualToString:@"ERROR"]) {return 1;};
if ([responseok isEqualToString:@"LAST"]) {return 2;};

firstline = 'N';
first_id_found = 'N';
tokenclass = nil;
*title_string = nil;

do {
    if (tokenclass == nil) {previous_tokenclass = nil;}
    if (tokenclass != nil) {previous_tokenclass = [NSMutableString stringWithString: tokenclass];}
    tokenclass = nil;
    token = [Entryfile1 GetNextToken];
    if (token == nil) {break;};

    tokenclass = [Entryfile1 ClassifyToken:token];

    if ([tokenclass isEqualToString: @"i"]) {
        [NoteTableArray addObject:[NoteTableEntry initNoteTableEntryForID:token]];
        firstline = 'Y';
        first_id_found = 'Y';
        savedheaderid = response;
        duplicate_count = 0;
        previous_tokenclass = nil;
        continue;};

    if (([tokenclass isEqualToString: @"s"]) & (first_id_found == 'N')) {
        *title_string = token;
        continue;};

    if (first_id_found == 'N') {continue;};

    if (([tokenclass isEqualToString: @"t"]) & ([previous_tokenclass isEqualToString: @"t"] == FALSE)) {
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        NoteTableEntrytemp = [NoteTableArray objectAtIndex:arrayCount];
        [NoteTableEntrytemp AddTimeEntry:savedheaderid withtimestring:token];
        continue;};

    if (([tokenclass isEqualToString: @"t"]) & ([previous_tokenclass isEqualToString: @"t"] == TRUE)) {
        duplicate_count++;
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        NoteTableEntrytemp = [NoteTableArray objectAtIndex:arrayCount];
        [NoteTableArray addObject:[NoteTableEntry initNoteTableEntryForID:[NoteTableEntrytemp NoteID]]];
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        NoteTableEntrytemp = [NoteTableArray objectAtIndex:arrayCount];
        [NoteTableEntrytemp AddTimeEntry:savedheaderid withtimestring:token];
        [NoteTableEntrytemp AddNoteType:savedheaderid withnotetype:@"t"];
        continue;};     

    if ([tokenclass isEqualToString: @"d"]) {
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        NoteTableEntrytemp = [NoteTableArray objectAtIndex:(arrayCount - duplicate_count)];
        [NoteTableEntrytemp AddNoteType:savedheaderid withnotetype:token];
        if ([token isEqualToString: @"n"] & (arrayCount > 0)) {
            NoteTableEntrytemp = [NoteTableArray objectAtIndex:(arrayCount - duplicate_count - 1)];
            temp_MarkTime = NoteTableEntrytemp.TimeMark;
            NoteTableEntrytemp = [NoteTableArray objectAtIndex:(arrayCount - duplicate_count)];
            [NoteTableEntrytemp setTimeMark: temp_MarkTime];
        }
        continue;};

    if (([tokenclass isEqualToString: @"s"]) & (firstline == 'Y')) {
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        loop_count = 0;
        do {
            NoteTableEntrytemp = [NoteTableArray objectAtIndex:(arrayCount - loop_count)];
            [NoteTableEntrytemp AddNoteHeader:savedheaderid withnoteheader:token];
            if (loop_count == duplicate_count) {
                tempstring = [NSString stringWithString: @" - XX:XX"];
                tempstring2 = [token stringByAppendingString:tempstring];
                token_xx = tempstring2;
                [NoteTableEntrytemp AddNoteBody:savedheaderid withnotebody:token_xx];}
            else {
                [NoteTableEntrytemp AddNoteBody:savedheaderid withnotebody:token];};
            loop_count++;

        } while (loop_count <= duplicate_count);
        firstline = 'N';
        continue;};

    if (([tokenclass isEqualToString: @"s"]) & (firstline == 'N')) {
        arrayCount = [ NoteTableArray count ];
        arrayCount--;
        NoteTableEntrytemp = [NoteTableArray objectAtIndex:(arrayCount - duplicate_count)];
        [NoteTableEntrytemp AddNoteBody:savedheaderid withnotebody:token];
        firstline = 'N';
        continue;};

} while (token);

return 0;

}

1
  • Because really my question never got answered. I was able to make the code work by using pointers to pass down the NSString. However, Entryfile did NOT require a pointer and the style of pass was different. If you got an answer for that, I'll give up the golden 'answer' box top. Commented Jul 24, 2011 at 14:48

2 Answers 2

4

Because Strings are immutable!

You can't change the value of a string at it's current location in memory. In your main program, you have a pointer to a location in memory which is storing the value "test string". In your method, when you assign a new value to title_string, you're pointing your local variable to a new location in memory. However, the value of title_string in your main program still points to the original location.

If, in your method, you said: Entryfile1 = [[Entryfile1 alloc] init];, my guess is you wouldn't expect the value of EntryfileName in your main program to change. You'd intuitively understand that in your main application you're still referencing the 'old' Entryfile - not the new one you just created. This is essentially what you are doing when you set title_string to a new value. You're only changing the local variable's pointer... not the pointer you have in your main program.

EDIT: To address the OP's comment about passing an NSString in an array

Consider the following app:

#import <Foundation/Foundation.h>

void foo (NSString ** stringRef);

int main (int argc, const char * argv[])
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    NSString * myString = @"Testing";

    NSLog(@"%@", myString);

    foo(&myString);

    NSLog(@"%@", myString);

    [pool drain];
    return 0;
}

void foo (NSString ** stringRef)
{
    *stringRef = @"Bar";
}

The result here, I believe, is what you're expecting. And I'm hoping that you'll see how this is different than what you are doing, and why this works (given my explanation above).

Sign up to request clarification or add additional context in comments.

18 Comments

It seems to me you're describing scope, not mutability. The same behaviour you describe would happen with a mutable object.
Fair enough but why does the NSMutableArray is seen as begin changed in the main program? If I wanted to be a dufus, I could drop my NSString into a NsMutableArray pass it back and forth that way. This just seems very odd to me.
@Jim - I am describing both scope and mutability (since the question he asked involves both). First, that the scope of those pointers are local in both cases, and Second, that the value of the pointer - not the values in memory the pointers are referencing - are what's changing in the case of the strings. Only an explanation which addresses both concepts would fully answer his question.
@joseph, Regarding this statement: "I could drop my NSString into a NsMutableArray pass it back and forth that way." Not true. You'd be passing a reference around. Not actual NSStrings. By changing the NSMutableArray, all you'd be doing is using a round-a-bout way of passing back and forth a pointer to a pointer.
@Steve, I don't see how mutability comes into your answer at all - as I said, you are describing a mistake that would be equally wrong regardless of mutability. You are assuming that the problem is that he's assigning to the local variable, whereas it's not obvious at all that this is the case. If it were, he'd probably be making the same mistake with the other variables as well. I've seen plenty of people assume that methods like stringByAppendingString: change the original instance - he could well be doing that. Strings aren't immutable like you say - only NSStrings are.
|
3

You can't change NSStrings. Not even within the method you are calling. They are immutable. If you are calling something like stringByAppendingString: on the string, that doesn't modify the string, it returns a new NSString object.

Chances are, you want NSMutableString.

2 Comments

The thread is getting a bit confusing but you're making sense, Steve. I'll add that code in for the NSString and I expect that it will work ok. My only remaining question is why does the NSMutableArray get altered without resorting to passing pointers? I think THAT was the source of most of my confusion.
I think you meant to post this as a reply to the other answer. Your original code passes the array and the string as pointers. You are doing something wrong in the function - we can't really say what without seeing it - which results in the string not being modified. Steve's solution is to pass a pointer to a pointer in the string's case, so you end up with an entirely new string object instead of trying to change the one you already have.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.