In order to better understand the startup, event queue, and methods within my application I'm trying to write a program that does two things: Play a beep at the startup and every time the user hits a button. So far it only plays when the user hits the button. I know there may be multiple ways to get the startup beep to play, but in order to work with initialization code I want to do it by calling my beep method from within the applicationDidFinishLaunching method of the AppDelegate.m file.
Here is my code:
Log.h
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m
#import "Log.h"
#implementation Log
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[self beepAndLog];
}
#end
And the applicationDidFinishLaunching method looks like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[Log beepAndLog];
}
In the applicationDidFinishLaunching method, however, XCode warns me that
'Log' may not respond to '+beepAndLog'
and indeed, there is no beep and the log reads as follows:
MethodResponse[11401:a0f] +[Log
beepAndLog]: unrecognized selector
sent to class 0x100002100
("MethodResponse" is the name of my project, btw)
I'm unsure why Log wouldn't respond to beepAndLog, seeing as that's one of its methods. Am I calling it incorrectly? I have a feeling this will be painfully obvious to you more experienced people. I'm a newbie. Any help would be appreciated! Thanks!
There are two possibilities. Either you defined beepAndLog as an instance method, when you wanted a class method, or you want to call it on an instance when you called it on the class.
To change it to a class method, change the header to read:
+(void)beepAndLog;
and the implementation:
+(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
For the other solution, make sure you have an instance of class Log around (probably a singleton), and do something like:
[[Log logInstance] beepAndLog];
from your notification method. The Log class would need to look something like this:
Log.h:
#import <Cocoa/Cocoa.h>
#interface Log : NSObject {
IBOutlet id button;
}
+(Log *)logInstance;
-(void)beepAndLog;
-(IBAction)buttonPressed:(id)sender;
#end
Log.m:
#import "Log.h"
Log *theLog = nil;
#implementation Log
+(Log *)logInstance
{
if (!theLog) {
theLog = [[Log alloc] init];
// other setup (like hooking up that IBAction)
}
return theLog;
}
-(void)beepAndLog {
NSLog(#"The Method Was Called!");
NSBeep();
}
-(IBAction)buttonPressed:(id)sender {
[[Log logInstance] beepAndLog];
}
Related
I am relatively new to Cocoa programming. Basically, I want to send a message within a method in my Document class to an intense of a class (that inherits from NSView) that I have initialised as a property in the #interface of the Document class.
Here is the simplified version:
///////////////////////////KOZDocument.h///////////////////////////
#import <Cocoa/Cocoa.h>
#import "KOZOtherClass.h"
#interface KOZDocument : NSDocument
#property (assign) IBOutlet KOZOtherClass *otherClassInstance; //this would be connected to the relevant CustomView in the IB
#end
///////////////////////////KOZDocument.m///////////////////////////
#import "KOZDocument.h"
#implementation KOZDocument
- (id)init
{
self = [super init];
if (self) {
// I want to send a message to otherClassInstance from some method e.g. init
NSLog(#"INITIALISING");
[[self otherClassInstance] printMessage];// this is the message I want to work but which doesn't (even though i don't any errors)
//sending the message to a locally initiated instance works but I don't want to use a local instance because i want to connect it to a CustomView in IB
KOZOtherClass *otherClassLocalInstance = [[KOZOtherClass alloc] init];
[otherClassLocalInstance printMessage];
}
return self;
}
//.….
///////////////////////////KOZOtherClass.h///////////////////////////
#import <Foundation/Foundation.h>
#interface KOZOtherClass : NSView
- (void) printMessage;
#end
///////////////////////////KOZOtherClass.m///////////////////////////
#import "KOZOtherClass.h"
#implementation KOZOtherClass
- (void) printMessage{
NSLog(#"This method can be called!!");
}
#end
/////////////////////////////////////////////////////////////////////
The same methodology works for all the native Cocoa objects but not for mine.
Can anyone tell me what I'm doing wrong?
Here is the context of why i want to do this:
I am building an app that plays a video using the AVFoundation. I have an animation I want to trigger in an NSView when the playback reaches a particular part in the video (e.g. after 2 seconds). I am adapting Apple's AVSimplePlayer and using the time observer to get the position of the playhead. The time observer executes the code inside a block for every given time interval. In this block I want to send a message to my animation view to trigger the animation when the time is more that 5 seconds for example.
In -init of your objects the Interface Builder connections are not set yet, the loading mechanism can't set those before your object is initialized.
Instead you want to overwrite the -awakeFromNib method like so:
- (void)awakeFromNib {
[[self otherClassInstance] printMessage];
}
-awakeFromNib is guaranteed to be called after the connections have been made. Depending on the exact implementation you may also need to guard against that code being executed twice, for example by having a boolean instance variable didWake that you check/set in that method.
SORRY FOR THE LENGTH OF THIS POST; IT IS MEANT TO DOCUMENT MY JOURNEY WITH THIS PROBLEM.
I have a question about a shared object in a Cocoa app that needs to change from time to time and how best to store it so that it's accessible from a few different places. Bear with me.
Class Implementation
The shared object is implemented as a Class Cluster (i.e., https://stackoverflow.com/a/2459385/327179) that looks like the following (note that Document is merely a class name; it is not necessarily indicative of what my actual class does):
In Document.h:
typedef enum {
DocumentTypeA,
DocumentTypeB
} DocumentType;
#interface Document : NSObject {}
- (Document *) initWithDocumentType:(NSUInteger)documentType;
- (void) methodA;
- (void) methodB;
#end
In Document.m:
#interface DocumentA : Document
- (void) methodA;
- (void) methodB;
#end
#interface DocumentB : Document
- (void) methodA;
- (void) methodB;
#end
#implementation Document
- (Document *)initWithDocumentType:(NSUInteger)documentType;
{
id instance = nil;
switch (documentType) {
case DocumentTypeA:
instance = [[DocumentA alloc] init];
break;
case DocumentTypeB:
instance = [[DocumentB alloc] init];
break;
default:
break;
}
return instance;
}
- (void) methodA
{
return nil;
}
- (void) methodB
{
return nil;
}
#end
#implementation DocumentA
- (void) methodA
{
// ...
}
- (void) methodB
{
// ...
}
#end
#implementation DocumentB
- (void) methodA
{
// ...
}
- (void) methodB
{
// ...
}
#end
How The User Interacts with a Document
Via a menu item, the user can switch between DocumentA and DocumentB at will.
What Happens When A "Switch" Occurs
When the user switches from, say, DocumentA to DocumentB, I need two things to happen:
My primary NSViewController (MainViewController) needs to be able to use the new object.
My AppDelegate needs to update an NSTextField that happens to be located in the content border of the main window. (FWIW, I can only seem to assign an outlet for the NSTextField in the AppDelegate)
The Question(s)
I've seen singletons mentioned quite a bit as a way to have a global reference without cluttering up one's AppDelegate (primarily here and here). That said, I've not seen much info on overwriting such a singleton (in our case, when a user switches from DocumentA to DocumentB [or vice versa], this global reference would need to hold the new object). I'm not an expert on design patterns, but I do remember hearing that singletons are not meant to be destroyed and recreated...
So, given all this, here are my questions:
How would you store my Class Cluster (such that MainViewController and AppDelegate can access it appropriately)?
Am I mixing concerns by having both MainViewController (who uses Document heavily) and AppDelegate (who manages the primary window [and thus, my NSTextField]) have knowledge of Document?
Feel free to let me know if I'm thinking about this problem incorrectly; I want this implementation to be as orthogonal and correct as possible.
Thanks!
Status Update #1
Thanks to advice from #JackyBoy, here's the route I've taken:
Document is the one that, upon "switching", "notifies" AppDelegate and MainViewController by passing them the newly created instance.
Both AppDelegate and MainViewController can update the Document object via the Singleton instance as necessary.
Here are my new files (dumbed down so that y'all can see the crux of the matter):
In Document.h:
#import <Foundation/Foundation.h>
#class AppDelegate;
#class MainViewController;
typedef enum {
DocumentTypeA,
DocumentTypeB
} DocumentType;
#interface Document : NSObject
#property (weak, nonatomic) MainViewController *mainViewControllerRef;
#property (weak, nonatomic) AppDelegate *appDelegateRef;
+ (Document *)sharedInstance;
- (id)initWithParser:(NSUInteger)parserType;
#end
In Document.m:
#import "AppDelegate.h"
#import "Document.h"
#import "MainViewController.h"
#interface DocumentA : Document
// ...
#end
#interface DocumentB : Document
// ...
#end
#implementation Document
#synthesize appDelegateRef;
#synthesize mainViewControllerRef;
+ (Document *)sharedInstance
{
static XParser *globalInstance;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
// By default, I return a DocumentA object (for no particular reason).
globalInstance = [[self alloc] initWithDocumentType:DocumentA];
});
return globalInstance;
}
- (id)initWithDocumentType:(NSUInteger)documentType
{
Document *instance = nil;
switch (parserType) {
case DocumentTypeA:
instance = [[DocumentA alloc] init];
break;
case DocumentTypeB:
instance = [[DocumentB alloc] init];
break;
default:
break;
}
// QUESTION: Is this right? Do I have to store these references
// every time a new document type is initialized?
self.appDelegateRef = (AppDelegate *)[NSApp delegate];
self.mainViewControllerRef = self.appDelegateRef.mainViewController;
[self.appDelegateRef parserSwitchedWithParser:instance];
[self.mainViewControllerRef parserSwitchedWithParser:instance];
return instance;
}
#end
#implementation Xparser_NSXML
// ...
#end
#implementation DocumentA
// ...
#end
Should I be bothered by the fact that Document has knowledge of the existence of AppDelegate and MainViewController? Additionally, should I be bothered by the fact that when the Document object updates, it re-notifies both AppDelegate and MainViewController (even though one of those initiated the update)?
As always, I appreciate everyone's eyeballs on this as my quest for the ideal implementation continues. :)
Status Update #2
A comment from #Caleb helped me understand that an NSNotification-based setup would be a lot less unwieldy for this particular problem.
Thanks, all!
I don't see he need for a shared object here, much less a singleton. Do you really need to find the current Document at arbitrary times from many different objects? Seems more like you just have two objects (app delegate and view controller) that both need to know about the current Document. Notifications provide an easy way to manage that: whenever a switch happens, you can post a NSNotification that includes the new Document. Any objects that need to know about the current Document will have registered for the "document switch" notification, and when the notification arrives they can stash a pointer to the Document in an instance variable or property.
I do remember hearing that singletons are not meant to be destroyed
and recreated...
Well, you can have references inside of it, so you are not actually "destroying" the singleton, but the objects he points to. I tend to leave the App Delegate without application logic, so I normally put it somewhere else. In your case, since you need to access something from different places, it makes sense to have one. About the cluster, you can still have it, you just ask the singleton to access it and return the appropriate object like so:
Document *myDocument = [[MySingleton defaultManager] createObjectWithType:aType];
You gain some things out of this:
you can access your cluster from any place in your app
you decouple things, only one entity knows about your cluster.
Inside the Singleton you can have a reference to you AppDelegate and interact with it.
Inside the Singleton you can have a reference to the objects that are being used (Document A, Document B)
One more thing, I would advise putting the cluster access method as a class method (instead of an instance one).
I have a method implemented in the ViewCotroller class to set the message to a UILable in that class.
-(void)setAuthenticationMessage:(NSString *)message{
//lblStatus is the UILabel
lblStatus.text = message;
}
I'm accessing this method from another class to set the message. Though code executes correctly this message didn't update. I tried it by executing this method in a different thread. But that was also unsuccessful. Can anyone help me to figure out the issue?
What are lblStatus's memory management properties? Your naming convention alone leads me to believe that it is something to do with this. Usually you refer to instance variables as:
_lblStatus or self.lblStatus
use #protocol & delegates to access another class refer this link example example2
Use the delegate design pattern (google it). Example:
//In the updating class .h
#protocol LabelChangerDelegate <NSObject>
-(void)handleLabelChangeMessage;
#end
#property (assign, nonatomic) id <LabelChangerDelegate> delegate;
//In the updating class .m
-(void)changeLabelInOtherClass{
if (self.delegate != nil && [self.delegate respondsToSelector:#selector(handleLabelChangeMessage)]) {
[self.delegate performSelector:#selector(handleLabelChangeMessage)];
}
} else {
NSLog(#"Delgate doesn't implement handleLabelChangeMessage");
}
//In the view controller .h
<LabelChangerDelegate>
-(void)viewDidLoad{
//set the delegate to self
}
//In the view controller .m
-(void)handleLabelChangeMessage{
self.label.text=#"I changed through delegation";
}
Another option is to use NSNotificationCenter
I have created a method that is running in new thread.
[NSThread detachNewThreadSelector:#selector(setmostpopularReq:) toTarget:self withObject:mostPopulerstring];
After completed this method i send all data to main thread.
[self performSelectorOnMainThread:#selector(getmostpopularResponse:) withObject:self waitUntilDone:YES];
But some time my main thread method not calling.
i used
dispatch_sync(dispatch_get_main_queue(),^{[self getmostpopularResponse:mostPopularList];});
But this is also have the same problem some time its calling method or some time not calling.
Please help me in this.
I would advise you to create a delegate with which you could notify the main thread after the
completion of the detached thread
Also another solution would be to create an NSOperation and NSOperationQueue instead of a new thread. There you can schedule what you want. For me looks easier, though it depends on you.
Here is a link to help you more with NSOperation
https://developer.apple.com/library/mac/#featuredarticles/ManagingConcurrency/_index.html
I will write this really quickly.
#protocol RespondDelegate
- (void)notifyWithRespond:(NSData *)data;
#end
#interface ContactWebServiceOperation:NSOperation
#property (nonatomic, assign) id delegate;
#end
#implementation ContactWebServiceOperation
#synthesize delegate;
// initialize here.
- (id)initWithDelegate:(id)delegate;
{
if ([self = [super init]) {
self.delegate = delegate;
}
return self;
}
- (void)main
{
if (self.isCancelled) return;
if (nil != delegate) {
// Do your work here...
work();
// When finished notify the delegate with the new data.
[delegate notifyWithRespond:your_data_here];
// Or
[delegate performSelectorOnMainThread:#selector(processImageForDownloadOperation:)
withObject:self waitUntilDone:YES];
}
}
#end
// Now on the view that you want to present the received results
// you have to do one thing.
// Let's say that your view is called View1
#interface View1 : UIViewController<RespondDelegate>
// Here put whatever you like.
#end
#implementation View1
// Put here all your code.
- (void)notifyWithRespond:(NSData *)data
{
// Here you will handle your new data and you will update your view.
}
#end
If I understand correct this should work.
Also, you can change the NSData to whatever you like, as long as you perform the appropriate conversions later.
If it doesn't work take a look on the link from Apple, maybe I have some typo or something.
But in general it looks solid.
So, I have a decent idea of what a delegate does, why use it, how to implement it etc. and I'm working on implementing it in one of my projects. The problem I'm trying to solve is to decouple my Controller objects from my Network Access class. In this context, the ideas get a little messy in my head.
I somehow intuitively feel that the NetworkAccessClass should be the delegate for a Controller object, because the NetworkAccessClass is acting as a helper for the Controller object. But it seems to work in a reverse fashion, because the following is apparently the right way to do it:
NetworkaccessClass.h
#protocol NetworkAccessDelegate
-(void) requestSucceded:(NSData *) data
-(void) requestFailed:(int) responseCode;
#end
#interface NetworkAccessClass : NSObject
{
id<NetworkAccessDelegate> networkDelegate;
}
#property(nonatomic, assign) id networkDelegate;
-(void) initWithDelegate:(id) delegate; //
#end
NetworkAccessClass.m
#implementation
#synthesize networkDelegate
-(void) initWithParams:(id) delegate
{
networkDelegate = delegate;
// Assign GET/POST vals, create request etc
[request startAsynchronous];
}
-(void) requestSucceded:(ASIHTTPRequest *) request
{
if([networkDelegate respondsToSelector:#selector(requestSucceded:)]) {
// Send the data to the controller object for it to use
...
}
}
-(void) requestFailed:(ASIHTTPRequest *) request
{
// Same as above. Send to request failed.
}
#end
And finally in my FirstViewController.h
#import "NetworkAccessClass.h"
#interface FirstViewController<NetworkAccessDelegate>
{
}
-(void) requestSucceded:(NSData *) data;
-(void) requestFailed:(int) responseCode;
#end
And the same in SecondViewController.h and so on.
Although this does decouple my Controllers from my Network class, I can't help feel it's wrong because the controllers in this case are acting as delegates or helper methods to the Network Class and not the other way round. Am I missing something basic? Or is this how it is?
Thanks,
Teja.
Delegates aren't "helper methods". Think of them as objects that get notified when something happens. (Although don't confuse them with "Notifications"--that's a different thing entirely.) In this case, your network class does it's stuff and then calls its delegate method on the View Controller that instantiated and fired, it to report the contents of that response to the view controller. The controller will then, presumably, update the view with the data that the network connector got. Classic delegate pattern, right there.