Objective-C call function on another class? - objective-c

Here are my objective-c classes:
AppDelegate
SomeScript
How might I call the function loggedIn on the SomeScript class from the app-delegate or any other class?
Thanks,
Christian Stewart

(I'll assume loggedIn is an instance method taking no parameters.) First, several terminology issues:
They're not functions, they're methods (same idea, though).
You don't call methods, you send messages (usually same idea, though).
Most importantly, we usually send messages not to classes, but to instances of those classes. (If you can't visualize the difference, imagine placing a letter in the idea of mailboxes vs. placing a letter in your mailbox. Only one makes sense!)
So, our new plan is to first instantiate SomeScript, then send a message to the instance.
SomeScript* myScript = [[SomeScript alloc] init]; //First, we create an instance of SomeScript
[myScript loggedIn]; //Next, we send the loggedIn message to our new instance
This is good. However! I bet you want your script to stick around for later use. Thus, we should really make it an instance variable of your app delegate. So, instead, in AppDelegate.h, add this inside the braces:
SomeScript* myScript;
Now our variable will stick around, and our first line from before becomes simply:
myScript = [[SomeScript alloc] init];
Last complication: we don't want to create a new script every time we call loggedIn (I assume)! So, you should place the instantiation somewhere it will only be run once (for example, application:DidFinishLaunchingWithOptions:). Ta-da!

You shall have an initialized reference of a SomeScript object in your AppDelegate class (supposing you do not need SomeScript to be a Singleton class like your AppDelegate). Something like:
SomeScript * myScript;
as an ivar in your AppDelegate interface, while in its application:DidFinishLaunchingWithOptions:
you have inited it (let's suppose with the default alloc/init combo calling):
myScript = [[SomeScript alloc] init]
Done all of this, when you need to call a method of myScript you can simply do:
[myScript myMethod:myParameter]
Here you can find a nice guide for beginners from Apple

If you don't want to use instances of SomeScript ... you can follow a different approach. Use NSNotificationCenter for sending a notification to your SomeScript object and make it run a selector after that.
In your -(void)awakeFromNib{} method, from SomeScript place the following code :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mySelector:)
name:#"aUniqueNameForTheNotification"
object:nil];
Create the method "mySelector:" and place the the call to your loggedIn method. (Or if you prefer, you could replace "mySelector:" with loggedIn directly)
-(void) mySelector:(id)elem
{
[self loggedIn];
}
Then don't forget to remove the observer on dealloc, so place the following piece of code in your SomeScript class also :
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Then you can send a notification from any other like so :
[[NSNotificationCenter defaultCenter] postNotificationName:#"aUniqueNameForTheNotification" object:self];
That last piece of code sends a notification to SomeScript and your selector is executed.
Hope it helps you guys!

We can call it like [self loggedIn] When loggedIn method is in SomeScript class, using simple syntaxes in latest xcode.
[[SomeScript new] loggedIn];

Related

Objective-C remove all oberservers for a certain notification

I have an iPad app that uses a proprietary library object which registers for a "UIScreenDidConnectNotification". Occasionally this object is deallocated and reallocated behind the scenes. As it is in a library, I cannot ensure that it is properly removing this observer.
Is there a way for me to manually remove all/any observers for a specific notification (i.e. UIScreenDidConnectNotification) without having any access to the object that has registered. This would keep the application from sending the message to a deallocated object.
Update: Here is the easiest way to fix my problem. I wish I could do a better job, but life is too short.
#import
#import
#interface NSNotificationCenter (AllObservers)
#end
#implementation NSNotificationCenter (AllObservers)
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
// This function runs before main to swap in our special version of addObserver
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(addObserver:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_addObserver:selector:name:object:));
method_exchangeImplementations(original, swizzled);
}
/*
Use this function to remove any unwieldy behavior for adding observers
*/
- (void) swizzled_addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender
{
NSString *notification = [[NSString alloc] initWithUTF8String: "UIScreenDidConnectNotification" ];
// It's a hack, but I just won't allow my app to add this type of notificiation
if([notificationName isEqualToString: notification])
{
printf("### screen notifcation added for an observer: %s\n", [notificationSender UTF8String] );
}
else
{
// Calls the original addObserver function
[self swizzled_addObserver:notificationObserver selector:notificationSelector name:notificationName object:notificationSender];
}
}
As it is in a library, I cannot ensure that it is properly removing this observer.
If the object is created in a library, it's not your responsibility to remove the object. If the library is deallocating the object without removing it from the notification center, that's a clear bug in the library.
Is there a way for me to manually remove all/any observers for a specific notification... without having any access to the object that has registered.
There's nothing in the API for NSNotificationCenter that lets you do that. Just the opposite, in fact -- the methods that let you remove the observer all require a pointer to a specific object.
I agree with both of Caleb's points: it is not your responsibility to perform this task and there is nothing in the API to support it.
However... if you feel like hacking something in to perform this task for whatever reason, refer to this thread: How to retrieve all NSNotificationCenter observers?
The selected answer of that thread has a category for NSNotificationCenter that allows you to retrieve all observers for a given notification name. Again, this is not recommended though.

Logging NSNotifications

I would like to log any NSNotifications posted by a single NSNotificationCenter shared accross my application. I have tried subclassing NSNotificationCenter with the intention of adding logging code to the three post methods, but it returns an instance of CFNotification center instead of my subclass.
Surely there is a way of monitoring NSNotification posting?
EDIT/UPDATE
As two answers below correctly point out I could listen to all notifications and log them in a handler, but the sequence the handler would receive these notifications is far from guaranteed to be the same as the sequence in which they were dispatched. If I could be sure the handler would always be the first hander to be notified this would work, but I cannot: 'The order in which observers receive notifications is undefined' From NSNotification Docs
By using - addObserver:selector:name:object: and passing nil for both the name and the object, you will get notified about any notification.
- (id)init
{
self = [super init];
if (self != nil)
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(log:) name:nil object:nil];
}
return self;
}
- (void)log:(NSNotification *)notification
{
NSLog(#"%#", notification);
}
Edit: if you want to get the real order of the notifications being send, try subclassing NSNotificationCenter and overriding the following methods:
– postNotification:
– postNotificationName:object:
– postNotificationName:object:userInfo:
If subclassing is no option for you, you might consider defining a category on NSNotificationCenter where you override these methods with calling the super implementation. (you will need to swizzle methods to call super within a category). Tell me if you need help doing so.
You should be able to use [addObserver:self selector:#selector(whatever:) name:nil object:nil] and just put your logging code in the whatever: method. This observer should get all notifications posted by your app (at least all those posted by the default center).

register a class in objective c

lets say i have classA which is a class of audio,that sample the audio input many times.
each time class A get a new data (can happen many times in second), he needs to inform another class, which is classB.
Now, i could just make an instance of class B in classA and call B when there is a new data arrived, but this is not a modular software.
i want classA to be "blind" to the outside, and just to add him to every project, and to have another classB that will register him some how, so when A has something new, B will know about it,(without A calling B ! )
how its done right in objective c ?
thanks a lot .
Sounds like you want to implement the Observer Pattern
You can post a notification in ClassA, and register for that notification in other classes (namely ClassB).
This is how you can do it:
(in ClassA):
[[NSNotificationCenter defaultCenter]
postNotificationName:#"noteName" object:self];
(in ClassB):
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(doSomething:)
name:#"noteName" object:nil];
Whenever an instance of ClassA posts a new notification, other instances that have registered to that notification will be informed (instantaneously). In this case, ClassB will perform doSomething:(NSNotification *)note.
[Edit]
You can post that notification your setter method (setVar:(NSString*)newVar).
If you want to pass something along, use the postNotificationName:object:userInfo: variant. userInfo is a NSDictionary and you can pass anything you want in it. for example:
NSDictionary* dic = [NSDictionary dictionaryWithObjectsAndKeys:var, #"variable", nil];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"noteName" object:self userInfo:dic];
now, edit your doSomething: method:
-(void)doSomething:(NSNotification*)note {
if ([[note name] isEqualToString:#"noteName"]) {
NSLog(#"%#", [note userInfo]);
}
}
More info:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Notifications/Introduction/introNotifications.html
https://developer.apple.com/library/mac/#documentation/General/Conceptual/DevPedia-CocoaCore/Notification.html
https://developer.apple.com/library/mac/#documentation/Darwin/Conceptual/MacOSXNotifcationOv/Introduction/Introduction.html
As ennuikiller suggested, an easy way to implement an observer pattern in obj-c is to use NSNotificationCenter class. For further info see its class reference.
Edit
An other way is using KVO (Key Value Observing). This is more complicated but has better performances respect to the first one. For a simple explanation see Jeff Lamarche blog and KVO Reference.
Hope it helps.

Obj-C using #selector on a static method from a static method in the same class?

I have two static methods/selectors in the same class, one passes the other as a callback to an external method. However, how I have it coded I get an error. This worked when both the methods were instance methods, and I've read it can work when the first method is an instance method using [self class]. However, I haven't found information when both are static, and I haven't got it to work.
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Thanks!
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
That line of code is setup to call the instance method Parse:, not a class method as you have it defined.
Objective-C does not have static methods. It has class methods and instance methods.
As well, your methods should start with lowercase letters.
Herp-da-derp. Dave is right.
Given this:
+(void)Validate {
Callback *managerCallback = [[[Callback alloc] initWithTarget:self Action:#selector(Parse:)] autorelease];
...
}
+(void)Parse:(Callback *)managerCallback {
...
}
Some comments:
methods should start with lowercase letters
it is exceedingly odd to use a class in such a role; even if you really only ever need one of 'em, use an instance. At the least, the instance is a convenient bucket to toss state in and it'll make refactoring in the future much easier if you ever need two.
The above pattern makes the assumption (and I ASSumed) that the instance of Callback is retained. For callbacks, timers, and some other patterns, this is typical; retain the target until the target is called for the last time. Then release (or autorelease). However, notification centers do not do this. Nor are delegates retained, typically.
Turns out the code is written correctly to do what I wanted to, but because callback was set to autorelease, the object was getting released before the callback was being processed.

Enforcing that a class posts a particular NSNotification?

Is there any way to ensure that a class posts a particular NSNotification?
(I have a set of classes, and I would like to enforce at compile-time (if possible) that the class posts a required NSNotification).
Alternatively, if that is not possible, is there any workaround?
It's fundamentally impossible to predict at compile time what will happen at run time. The closest you can get is static analysis, but even that can't predict anything that happens outside of your own code, such as inside Foundation.
You can, however, do this with unit tests, since the test runner actually runs the code under test.
You'll need to create a test bundle target, if you haven't already. Your target will use SenTestingKit to run your tests, which you create. (On the iPhone, you'll also need Google Toolbox for, uh, Mac. They have a handy tutorial on using GTM for iPhone tests.)
You'll create a SenTestCase subclass to test whether your real object posts a notification. It'll look something like this:
#interface FrobnitzerNotificationsTest: SenTestCase
{
BOOL frobnitzerDidCalibrate;
}
- (void) frobnitzerDidCalibrate:(NSNotification *)notification;
#end
#implementation FrobnitzerNotificationsTest
- (void) testFrobnitzerCalibratePostsNotification {
Frobnitzer *frobnitzer = …;
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(frobnitzerDidCalibrate:)
name:FrobnitzerDidCalibrate
object:frobnitzer];
frobnitzerDidCalibrate = NO;
//This should post a notification named FrobnitzerDidCalibrate with the receiver as the object.
[frobnitzer calibrate];
//If it did, our notification handler set frobnitzerDidCalibrate to YES (see below).
[nc removeObserver:self
name:FrobnitzerDidCalibrate
object:frobnitzer];
STAssertTrue(frobnitzerDidCalibrate, #"Frobnitzer did not post a notification when we told it to calibrate");
}
- (void) frobnitzerDidCalibrate:(NSNotification *)notification {
frobnitzerDidCalibrate = YES;
}
#end
You'll need one instance variable and one notification-handler method for every notification you want to test for, and one test method for every method you want to test for notifications.
Also, if using GTM, you must substitute GTMSenTestCase for SenTestCase above.