0
votes

Please take a look at these two simple pieces of code. This

- (void)testMethod
{

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"myEncodedObjectKey"];
self         = (Profile *) [NSKeyedUnarchiver unarchiveObjectWithData:data];

for (int i = 0; i < self.avatar.count; i++)
    [self.avatar replaceObjectAtIndex:i withObject:[UIImage imageWithData:[self.avatar objectAtIndex:i]]];

if ([[self.avatar objectAtIndex:0] isKindOfClass:[UIImage class]])
    NSLog(@"UIImage");//at this moment it's UIImage
}

and this:

[currentProfile testMethod];

if ([[currentProfile.avatar objectAtIndex:0] isKindOfClass:[NSData class]])
    NSLog(@"NSData");//Moment later it is NSData

In the first one i fetch a custom object from the NSUserDefaults and work with a NSMutableArray variable named "avatar". I convert its each object from NSData to UIImage. Then i check what i've got by using NSLog . It's UIImage. At the second piece of code you can see how a moment later what was UIImage turns back to NSData by its own will. Seems like i described my issue clearly. Do you understand what's going on? I don't. Thanks a lot in advance for your attention

1

1 Answers

2
votes

Why are you changing the self object in your -testMethod method? This is highly illegal.

What you're actually doing is setting a local variable self, which is passed as a parameter to your method, to a new value. This means you're not editing the receiver of the method, you're just editing your parameter.

When your method is called at runtime the C function objc_msgSend() is called:

// Declaration of objc_msgSend
id objc_msgSend(id receiver, SEL selector, ...);

Now when you call your method ...

[myInst testMethod];

... this is what actually gets called at runtime:

objc_msgSend(myInst, @selector(testMethod));

Do you already see what's happening? In your method implementation the self variable is set to the first argument of objc_msgSend. When you're reassigning self, your not editing what the variable myInst contains and thus you're not editing your the original instance you passed. You're just setting myInst, aka self, a local variable, to your knew pointer. The caller of the function will not notice the change.

Compare you're code to the following C code:

void myFunction(int a) {
    a = 3;
}

int b = 2;
myFunction(b);
printf("%d\n", b);
// The variable b still has the original value assigned to it

The above code does the same you do:

// Variation on objc_msgSend
void myMethodWrittenInC(id myInst) {
    // Local variable changes, but will not change in the calling code
    myInst = nil;
}

MyClass *myObj;

myObj = [[MyClass alloc] init];
myMethodWrittinInC(myObj);
// At this point myObj is not nil

And finally this is what you do:

- (void)testMethod
{

    NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"myEncodedObjectKey"];
    // You assign the local variable self (passed as an invisible argument
    // to your method) to your new instance, but you do not edit the original 
    // instance self pointed to. The variable currentProfile does not change.
    self = (Profile *) [NSKeyedUnarchiver unarchiveObjectWithData:data];

    for (int i = 0; i < self.avatar.count; i++)
        [self.avatar 
         replaceObjectAtIndex:i 
         withObject:[UIImage imageWithData:[self.avatar objectAtIndex:i]]];

    if ([[self.avatar objectAtIndex:0] isKindOfClass:[UIImage class]])
        NSLog(@"UIImage");//at this moment it's UIImage
}


// (1) Here currentProfile points to an instance of your class
[currentProfile testMethod];
// (2) it calls the method, but the local variable does not change
// and still points to the same instance.

if ([[currentProfile.avatar objectAtIndex:0] isKindOfClass:[NSData class]])
    NSLog(@"NSData");//Moment later it is NSData