Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

FredAkbar

macrumors 6502a
Original poster
Jan 18, 2003
660
0
San Francisco, CA
Question for anyone that knows Cocoa:

Okay, I know C, and just learned Obj-C and some Cocoa. So I'm writing a text-adventure program (gotta start small!) in Cocoa using Xcode and Interface Builder. Everything's going well at the moment, except that the method that actually prints output text apparently only works when it's called from another method of the same class. Basically I made several subclasses of NSObject (I'd rather have multiple classes and code files, just for organizational purposes). Anyway, this method, pOutput:, which takes an NSString as its argument and prints it to an NSTextView in my app, works fine when it's called from another method of its class, but doesn't seem to work when I call it from another class (well, object).

Here's the method (I found the code online, somewhere or other):
Code:
- (void)pOutput:(NSString *)outputString
{
	NSLog(@"pOutput:; outputString = %@", outputString);
	NSRange selectionRange = NSMakeRange([[textView textStorage] length], 0);
	[textView setEditable:YES];
	[textView setSelectedRange:selectionRange];
	[textView insertText:outputString];
	[textView setEditable:NO];
	[textView display];
}

Using that NSLog, I've assured that it always gets its argument (outputString) properly, no matter where it's called from, and that the calling method is always able to find pOutput. The only problem is actually displaying the text itself. textView is an instance variable (a pointer to my NSTextView) of the same class that contains pOutput. Xcode never gives me any warnings with this or anything, and like I said, it works fine when it's called from the same class (and therefore same source file), but doesn't work when it's called from some other class.

Thanks to anyone who can help me with this. Obviously a temporary solution would just be to give each class its own pOutput method to call for itself, as in [self pOutput:mad:"print this text"];, but I'd like to know what's wrong here.

edit: I'm not fully correct in this post; see next post.
 
correction

Correction: I said that my pOutput method only works when called from another method of its class (its class is called EventHandler). This isn't quite true now, I've realized. I made an identical method called showText: in another of my classes (called RoomCode), but even when an instance of RoomCode calls [self showText:aString]; it still won't print anything. (showText gets the value for textView with [eventHandler textView];, where eventHandler is an instance of EventHandler and textView is a method of eventHandler which returns textView--textView is originally defined in EventHandler) Now it seems that the only time it will actually print output in my NSTextView is when a) the calling method is a method of EventHandler (the class that orginally contained pOutput)...and b) the called method (pOutput or showText or whatever) is also in EventHandler.

Each loop (processing of input) starts in EventHandler, as one of its methods, handleInput, is called whenever the user enters text (my input field object has handleInput as its action, which it calls when the user "sends" text by hitting return or enter). EventHandler later calls a method of RoomCode--that's when I know that my RoomCode methods aren't able to have my program print anything.

I am fairly new to Cocoa so I'm somewhat clueless here :confused:.

update: I've found out that it's a problem with the textView pointer. According to the debugger, when I ask for the value of textView (meaning when I type po textView at the debugger console prompt) when I'm in showText (the method that isn't in EventHandler), it says "cannot return memory at address 0x0" or something.

another edit: honestly, is anything wrong with this method?
Code:
- (NSTextView *)textView
{
	return textView;
}
Other methods of this class can use textView just fine, but this one seems to have trouble with it. In the debugger, if, right in the middle of this method, I say "po textView" it tells me the address 0x0 thing.

[final edit: fixed it, sort of; in handleInput, which calls the instance of RoomCode using [roomCode methodName]; (the method in RoomCode isn't really called methodName, it's just an example), I simply pass textView as a parameter to methodName; I also made RoomCode its very own textView variable, which gets set to the argument that methodName receives when it's called.

I still don't know why I have to do this, and why handleInput knows textView's value, while the textView method doesn't (and both are methods of the same class). Anyway, I'm probably just talking to myself at this point, but if anyone can enlighten me on what's going on here, I'd appreciate it...thanks.
 
whew. I've read that a few times and I'm still lost!

I think I'd have to see the code to tell what was wrong, but I just posted to say that as far as I know there is no need to change [textview setEditable:YES/NO] to insert your text, as this refers to whether the end user is able to edit the text, not the program. I also don't think the [textview display] is necessary (but it may be as I've only ever changed a textview using [textview setString:aString]).
 
Yeah, sorry about the confusion...

I took out the setEditable:NO and :YES lines, and it stopped working, so I think those do matter. But you're right about [textView display]; not being needed; I probably added that in one of my vain attempts to fix my problems.
 
I don't really feel like changing the code back to how it was, and I don't really want to just post a lot of the code online, so basically I can only make it as clear as I can...

When the user enters text (and hits return) into the input field in my app (an NSTextField), it triggers the handleInput: method of the EventHandler class. Also in the EventHandler class is the pOutput: method. When handleInput: calls pOutput with [self pOutput:aString];, everything works fine. But when a different class calls pOutput with [eventHandler pOutput:aString]; (it's eventHandler, not EventHandler, because it's calling an instance of the EventHandler class, named eventHandler), it doesn't display the text in my NSTextView.

Another (somewhat separate) problem is, say I decide to give each class its own method for printing output, so any class or object can always just use [self pOutput] (if that's what I always call the method)...well, each of these methods would still need to access the textView pointer so that it knew what NSTextView to change. The textView pointer is an instance variable native to the EventHandler class, but strangely, if I make a method in EventHandler called textView (which returns the textView variable), this textView method simply doesn't know the variable's value (the memory address of my NSTextView object) when it comes time to return it to some other class who is asking for it. So my only option is to make a global variable called, say, txtView, and directly in handleInput, set txtView = textView. txtView, as a standard global variable that isn't connected to Interface Builder or any specific class, always works fine.

Still confusing, I know...I'll try and just make a fresh project with only the stuff I'm mentioning here, if you're curious.
 
Okay, I just made a minimal project with both problems I've mentioned. Download it and build/run it. Type stuff into the app window's input field and hit return. You can check out the code too if you want (obviously). The only text that gets printed is the stuff called within EventHandler to the pOutput: method which is also in EventHandler.
 

Attachments

  • myapp.zip
    20.1 KB · Views: 65
Hi FredAkbar,

Could you post a URL to a .sit or .zip of your source file? That might be really helpful. Although we can abstract source code by verbalizing the workflow/logic/design in English, the translation is faulty. The code is the code is the code and is the product of your perception of workflow/logic/design.

The code itself a better description than trying to describe it.

Perhaps a useful analogy is asking someone to help you accurately translate an old Latin text into English by showing them your incorrect English translation. The most useful text would be the original Latin text.

Best,
Logicat
 
logicat: you like analogies, don't you? :p (I remembered seeing your screen name recently somewhere...you also posted in the thread of that guy who wanted to learn more about programming, and you used an analogy there too...but hey, they sure work in getting your point across.)
 
:confused:

Wow. I'm beginning to hope that it's not viewed as a negative. I've always spoken/written/thought like that, for as long as I can remember.

It's funny: yesterday afternoon I was with a client of mine and two of the employees remarked that had I been around, I would have phrased the problem as an analogy.

It's like growing up in the deep South, with a thick and heavy Southern drawl, but invisible to my own ears; until suddenly being made aware of it while visiting friends in Canada. Now even I can hear it and I question whether or not it's an appealing affectation.

;)

As for your coding frustrations, I'm not going to be checking it out until later in the week. A few deadlines lined up in a row.

Best,
Logicat
 
Oh, no, there's nothing bad about it at all, it's a very good way to explain something...it was just funny for me, seeing two different posts by you in different programming-related threads within a day or two, both using analogies to make your point. It's a good thing, though, don't worry :)...sorry if you misunderstood.
 
Try [textView setStringValue:eek:utputString]; , I didn't read your second post or your first one very good so that may not work

The smile is a :<nospace>o
 
Doesn't setStringValue:eek:utputString just change the entire contents of the NSTextView to be outputString? I would like for my output to simply be added to what's already there; that's why I have to use insertText:.
 
I couldn't get the project to open in project builder, but after looking at the code a little bit your roomthing instance was never allocced. sorry I can't help more, but I cant open the project
 
It's an Xcode file, so that's probably why you can't open it. Sorry!

The RoomCode instance is allocated in main() (in main.m) with roomCode = [RoomCode new];

This command is the same as roomCode = [[RoomCode alloc] init];
 
So you're wondering why the text that is supposed to be put on the screen from RoomCode:wayStation is not being output even though it's calling eventHandler:pOutput?
 
I fooled around a bit with your project and decided your basic design was flawed (no offense or anything, I understand you're still learning). I reworked things according to the way I learned to do them in Cocoa and I attached a zipped project with my changes. It doesn't answer your questions per sé, but I still think that if you can understand and adopt my changes, then your problems will cease.

Let me know if you have any questions.
 

Attachments

  • MyApp.zip
    26.8 KB · Views: 58
That's cool that you got it to work, I didn't think of the other classes and objects just returning strings to EventHandler...thanks for that idea.

One question...you put the roomCode and inputString pointers as instance variables of EventHandler, so what if some other object wants to use them? Should I implement methods in EventHandler called roomCode and inputString, that return those pointer values to the calling object--so if, say, in RoomCode, I want to know what inputString is, I would have to put [EventHandler inputString]; ? I'd rather just use the object name itself; that's why I initialized inputString and roomCode in main.m, so they're accessable to all objects (I can always put "extern id *roomCode;" (without the quotes) in the header files for my other classes).
 
FredAkbar said:
That's cool that you got it to work, I didn't think of the other classes and objects just returning strings to EventHandler...thanks for that idea.

One question...you put the roomCode and inputString pointers as instance variables of EventHandler, so what if some other object wants to use them? Should I implement methods in EventHandler called roomCode and inputString, that return those pointer values to the calling object--so if, say, in RoomCode, I want to know what inputString is, I would have to put [EventHandler inputString]; ? I'd rather just use the object name itself; that's why I initialized inputString and roomCode in main.m, so they're accessable to all objects (I can always put "extern id *roomCode;" (without the quotes) in the header files for my other classes).

If you want a single global object of a certain class then use the singleton design patern instead of the very C way you are using. It works as follows:

in myobject.h
Code:
+ (id) sharedInstance;

in myobject.m
Code:
static myobject* _sharedBrickController;

+ (id) sharedInstance
{
    if (!_sharedBrickController)
    {
        _sharedBrickController = [[myobject alloc] init];
    }

    return _sharedBrickController;
}

to call a method on the object
Code:
[[myobject sharedInstance] anotherMethod];
 
FredAkbar said:
That's cool that you got it to work, I didn't think of the other classes and objects just returning strings to EventHandler...thanks for that idea.

One question...you put the roomCode and inputString pointers as instance variables of EventHandler, so what if some other object wants to use them? Should I implement methods in EventHandler called roomCode and inputString, that return those pointer values to the calling object--so if, say, in RoomCode, I want to know what inputString is, I would have to put [EventHandler inputString]; ? I'd rather just use the object name itself; that's why I initialized inputString and roomCode in main.m, so they're accessable to all objects (I can always put "extern id *roomCode;" (without the quotes) in the header files for my other classes).

My advice is that you only share static objects (as robbieduncan has demonstrated) or messages. In the case of your example, you could do what robbieduncan did, or you can pass inputString as a parameter to other object methods, let them process it, and return a result.

Cocoa is designed to make it easy to write programs based on the Model-Contoller-View pattern. You create the View and all instances of associated objects in Interface Builder, then you flesh out the controller (EventHandler) and the model (RoomCode) objects. The controller is meant to be the grand coordinator of the other objects: getting input from and outputting to the view layer, and using the Model layer objects as data processors and data stores.
 
Hmm, okay, I'll consider that for future apps, this one's really small and getting close to being done (not sure if I'll release it as freeware or not).

Thanks for all your help.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.