Cocoa question

Posted:
in General Discussion edited January 2014
Hey, I'm hoping some Objective-C/Cocoa maven can help me with a question. I feel like the answer should be obvious and easy, but so far it's not been so to me.



How do I use the sender of a message as an argument in that message?



I want one object to send a reference to itself as an argument for a method it invokes in a second object. I tried using "self", as in [secondObject method:self], but it produces an error at runtime ("selector not recognized", generated by the sending object).



The reason I want to do this is that I want an object to be able to call up the Undo Manager of the NSDocument that created it. Not being able to find any way to call the creator directly, I thought I would just add a "creatorObject" variable to the child, and have the parent fill it (sending itself as the argument to setCreatorObject) when the parent creates each child. No dice.



It seems like this should be such a simple thing, but I've been beating my head against Google trying to figure it out. Any help is much appreciated! Thanks!



[edited for precision]

Comments

  • Reply 1 of 7
    seems like that should work....



    selector not recognized implies to me that the receiver of your message has not publicly declared that method in the .h file ( -(void)methodid)sender ) or that the sender has not imported the receivers .h/interface.



    There may be a better way of doing this but i have no experience with the undo manager.



    Are you sure your object exists at the time it is sent the message?





    like maybe:





    newObject = [[MYNewObject alloc] init];

    [newCO setCreatorObject:self];



    or



    newObject = [[[MYNewObject alloc] init] setCreatorObject:self];



    or



    newObject = [[MYNewObject alloc] initWithParent:self];





  • Reply 2 of 7
    toweltowel Posts: 1,479member
    Quote:

    Originally posted by havanas

    seems like that should work....



    selector not recognized implies to me that the receiver of your message has not publicly declared that method in the .h file ( -(void)methodid)sender ) or that the sender has not imported the receivers .h/interface.




    It should work, right?



    I import the receiver's .h in the sender's .h. The method is declared like so:

    - (void)setParentDocid)sender;

    And defined like:

    - (void)setParentDocid)sender {

    \tsender = [sender copy];

    \t[parentDoc release];

    \tparentDoc = sender;

    }

    (parentDoc is declared in the .h as "id parentDoc;")



    The object (Day "d") is allocated by the interface (button bound to an array controller). I can add it to an array no problem, so it seems to exist. The context of the message is:

    \t[d setParentDoc:self];

    \t[self startObservingDay:d];

    \t[tripDays insertObject:d atIndex:index];



    But I get the runtime error... And it's definitely that one line of code ("[d setParentDoc:self];"), because if I comment it out, no error and everything else works fine.
  • Reply 3 of 7
    hirohiro Posts: 2,663member
    You are doing a shallow copy which leaves d, parentDoc and self all referring to the same identical object. -copy returns the object itself, not a new but functionally identical copy. So your line "[d setParentDoc:self];" is referring to itself as both sender and receiver, and then all over again in the opposite direction in the next line. That's confusing enough to look at let alone for the run-time engine to work out explicitly. In order to do what you want you need more than just self to work with, you need to create a distinctly different object somewhere along the line for the new doc to store the undo version of self into.
  • Reply 4 of 7
    toweltowel Posts: 1,479member
    Quote:

    Originally posted by Hiro

    You are doing a shallow copy which leaves d, parentDoc and self all referring to the same identical object. -copy returns the object itself, not a new but functionally identical copy. So your line "[d setParentDoc:self];" is referring to itself as both sender and receiver, and then all over again in the opposite direction in the next line. That's confusing enough to look at let alone for the run-time engine to work out explicitly. In order to do what you want you need more than just self to work with, you need to create a distinctly different object somewhere along the line for the new doc to store the undo version of self into.



    OK, this is what's confusing me. What does "self" refer to when you use it as an argument? I've seen tutorials that suggest it refers to the sender. And you seem to be suggesting it refers to the receiver. If so, can you help me resolve the crux of my confusion:



    How do I pass the sender object as an argument?



    [Clarification in case my post confused you:

    MyDocument is the parent object. It creates Days, and I want it to tell each Day it creates who created it (MyDocument) by sending "self" to setParentDoc of each Day.

    Day is the object that gets created. It has a parentDoc instance variable and implements the setParentDoc method to set it.]



    Thanks for the help so far!



    Edit: I had the stunningly obvious idea to do a someObj = [self copy] and then pass someObj, rather than self, as the argument. But it didn't help - same runtime error.
  • Reply 5 of 7
    toweltowel Posts: 1,479member
    My confusion is part of a more general one, that being how do you pass information up the chain of ownership? It's easy to pass information down the chain - from one object to another object that's represented as an instance variable of the first. But how do you communicate upwards - from the object that represents an instance variable to the object that's its an instance variable in?



    Or does it make no sense to ever want to do that?
  • Reply 6 of 7
    hirohiro Posts: 2,663member
    self is self, wherever you are and it will always be the current object, which means self can be different objects depending on where the self is used.



    Your real problem is with shallow vs deep copying. You are doing a shallow copy which is really only passing the pointer to the object into a new variable so both variables point to the same exact object--think that object as now having an alias. Deep copying creates an entirely new object which has the same data values, but not the same objects within. So your shallow copy is referring to itself only from the second name, while that object is re-referencing itself in a circular fashion.



    You need to ensure your new document is it's own completely created independent object, not merely copied using the defaut Object -copy method. Then you can pass the original object in s an argument and not have a problem.





    Code:


    doc1 = [[document alloc] init];

    //do some stuff



    //now I want an undo possible

    doc2 = [[document alloc] init];

    /* make everything inside doc2 look like doc1 except the change, this can even write the init to do this--but DO NOT USE THE DEFAULT -copy message */



    [doc2 setParentDoc:doc1]; //don't use self or you send in doc2 to itself



    where:

    for the receiver:

    - (void)setParentDocid)sender {

    parentDoc = [sender deepCopy]; //parentDoc will return with a reference count of 1, need to retain not to loose it leaving this scope

    [parentDoc retain];

    }



    and for the sender:

    -(id)deepCopy {

    newCopy = [[Sender alloc] init]; //here Sender is the Class, not an instance

    [newCopy retain]; //makes sure the reference count is one once you leave this scope and prevents losing the object prematurely

    [newCopy setVariable1: [self variable1]];

    //for every variable you have



    return newCopy;

    }











    And make sure you do good housekeeping with the dealloc's when the time comes or you will leak memory.
  • Reply 7 of 7
    toweltowel Posts: 1,479member
    Quote:

    Originally posted by Hiro

    Your real problem is with shallow vs deep copying.



    I shouldn't have even had the shallow copy in there. It was the result of my not understanding Hillegass's examples well enough. setParentDoc should have two statements only, "[parentDoc release]" and "parentDoc = sender". But I think that's irrelevant, anyway. I don't want to reinvent Undo. I just want to use a single undo manager for all of my interface-accessible objects, that being the undo manager of the parent document. Only because I think I need to, in order for the user to be able to undo all operations in their proper order from the Edit menu. I am clueless enough, however, that maybe I don't. Maybe Cocoa is smart enough that there is only one undo manager, no matter where you call it from.



    MyDocument contains an array of Object1's. Each Object1 contains an array of Object2's. Object1 and Object2 are different classes, completely unrelated. The user can create and edit any of the objects from the GUI. I use MyDocument's undo manager to undo edits to the Object1's. It's not clear to me how confusing things would be if I tried to use any given Object1's undo manager to undo edits to one of the Object2's. So I thought I would call MyDocument's undo manager from inside the method of Object1 that I want to be able to undo. Then I can add the operation to MyDocument's undo stack, and (I think) keep all my undos happily in one place. But I can't figure out how to do that.



    Sorry for the confusion. 90% of solving any problem is learning to express the proper question.
Sign In or Register to comment.