I am accessing a dispatched notification like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleUnpresent:) name:UNPRESENT_VIEW object:nil];
...
-(void)handleUnpresent:(NSNotification *)note;
{
NSLog(#"%#", note.object.footer);
//property 'footer' not found on object of type 'id'
}
Some of the incoming note.object objects have a "footer" and some don't. However, I don't want to go through to trouble of making a class that only has a property called footer just to make this work. I even tried ((NSObject *)note.object).footer) which works in some languages, but apparently not obj-c. What can I do?
Checking the isKindOfClass is certainly the more robust option. However, if you have multiple unrelated classes that return the property you need, there is another way: respondsToSelector. Just ask if the object has a footer method, and you can safely call it.
-(void)handleUnpresent:(NSNotification *)note;
{
id noteObject = [note object];
if ([note respondsToSelector:#selector(footer)])
{
NSLog(#"Footer = %#", [noteObject footer]);
}
}
That respondsToSelector method is powerful and handy in the right places, but don't go wild with it. Also, it can't tell you anything about the return type, so the footer you get may not be of the class you were expecting.
The syntax for noteObject.footer and [noteObject footer] are easy to treat as equivalent. However, when the class of noteObject is unknown, the compiler will accept the latter but not the former. If noteObject has a defined class that doesn't usually respond to footer, it will give a warning, but still compile and run. In these cases, it is your responsibility to guarantee that the method will indeed exist when needed, and therefore that the method call won't crash at run time.
If the object passed in the notification may be one of a number of classes and you don't want to cast the object to a specific class you can use performSelector: to call the footer method on the object. If you wrap this call with a respondsToSelector: you'll avoid an exception if the object turns out not to have a footer method.
-(void)handleUnpresent:(NSNotification *)note;
{
if ([[note object] respondsToSelector:#selector(footer)]) {
NSString *footer = [[note object] performSelector:#selector(footer)];
NSLog(#"%#", footer);
}
}
Using performSelector will stop the compiler complaining that the method "'footer' not found on object of type 'id'."
NSObject doesn't have any property named footer, which is why the compiler is complaining. Casting an id back to an NSObject doesn't help. If you know the object is always going to be some custom object you've created, you can cast back to that and then call footer and the compiler won't complain. It's best to actually check tho. See the example below (for the example, I named the class that has the footer property ViewWithFooter, so rename appropriately):
- (void)handleUnpresent:(NSNotification*)note
{
ViewWithFooter view = (ViewWithFooter*)[note object];
NSParameterAssert([view isKindOfClass:[ViewWithFooter class]]);
UIView* footer = [view footer];
// Do something with the footer...
NSLog(#"Footer: %#", footer);
}
If you have a bunch of unrelated classes (i.e., not in the same class hierarchy) that all present a footer property, you'd be best served creating a protocol with the required footer property and casting the object to the protocol in the code example above and asserting it responds to the -footer selector.
Here's an example using the protocol:
#protocol ViewWithFooter <NSObject>
- (UIView*)footer; // this could also be a readonly property, or whatever
#end
- (void)handleUnpresent:(NSNotification*)note
{
id<ViewWithFooter> view = (id<ViewWithFooter>)[note object];
NSParameterAssert([view respondsToSelector:#selector(footer)]);
UIView* footer = [view footer];
// Do something with the footer...
NSLog(#"Footer: %#", footer);
}
Related
I have this myViewController, that instantiates instances of itself.
Currently, I have a UIButton, that triggers the method
-(void)somethingImportant
However, I want that somethingImportant to happen during the ViewDidLoad, so I don't have to push that button.
But if I put somethingImportant in the ViewDidLoad of myViewController, it is recursively called as many times I have a subview of myViewController.
I tried to put somethingImportant in the application didFinishLaunchingWithOptions: of my app delegate, but somehow that does't work either.
EDIT
So here's the code that might be relevant. I have this UIScrollView with a lot of subviews of myViewController:
- (void)configureScrollView
{
for (int i = 0; i < [self.childViewControllers count]; i++) {
...
myViewController * theSubview =[self.childViewControllers objectAtIndex:i];
....
[theScrollView addSubview:[theSubview view]];
}
}
What is the best approach to make sure that somethingImportant is called only once?
I have this class, that instantiates instances of itself.
This inherently sounds like a bad idea and can easily lead to recursion if you're not careful. Therefore I would suggest you rethink your logic. If you need multiple instances of a class, you should be managing those instances from outside that class, not from within.
However, if you're still insistent on doing this - you can do something similar to what sschale suggests and use a variable to keep track of whether you've called your method or not.
The thing is you'll need to define this variable as static in order for it to be stored at class scope, not instance scope.
For example:
static BOOL firstCalled = NO;
- (void)viewDidLoad {
[super viewDidLoad];
if (!firstCalled) {
firstCalled = YES;
[self foo];
}
}
Each subclass should be calling [super viewDidLoad], on up the chain, so that code really should only be called once.
However, if you need to make sure it executes only once, add #property (nonatomic) BOOL runOnce; to that file's interface, and then in -(viewDidLoad) do:
if(!self.runOnce) {
//all code that is only run once
self.runOnce = YES;
}
This question already has answers here:
NSInvocation for Dummies?
(4 answers)
Closed 7 years ago.
I came across this code in a project, where it is using NSInvocation. I want to know what it is supposed to do, and why would we ever need that. Simple explanations would be appreciated. I am posting the code.
// Public interface
#interface CCDelegateSplitter : NSObject
- (void) addDelegate: (id) delegate;
- (void) addDelegates: (NSArray*) delegates;
#end
// Private interface
#interface CCDelegateSplitter ()
#property(strong) NSMutableSet *delegates;
#end
#implementation CCDelegateSplitter
- (id) init
{
self = [super init];
_delegates = [NSMutableSet set];
return self;
}
- (void) addDelegate: (id) delegate
{
[_delegates addObject:delegate];
}
- (void) addDelegates: (NSArray*) delegates
{
[_delegates addObjectsFromArray:delegates];
}
- (void) forwardInvocation: (NSInvocation*) invocation
{
for (id delegate in _delegates) {
[invocation invokeWithTarget:delegate];
}
}
- (NSMethodSignature*) methodSignatureForSelector: (SEL) selector
{
NSMethodSignature *our = [super methodSignatureForSelector:selector];
NSMethodSignature *delegated = [(NSObject *)[_delegates anyObject] methodSignatureForSelector:selector];
return our ? our : delegated;
}
- (BOOL) respondsToSelector: (SEL) selector
{
return [[_delegates anyObject] respondsToSelector:selector];
}
#end
I'll assume you know what an NSInvocation is (if not, it's a data structure that holds all the information needed to make a method call; think "blocks" from long before blocks were added to the language).
forwardInvocation: is one of the methods that the runtime will call if it cannot find an implementation for a method. So if you pass a -doSomething message to an object [object doSomething], it will first check whether it has a doSomething method. Then it will check its superclasses. It'll try dynamic method resolution (resolveInstanceMethod for instance). It'll look for a forwarding target (forwardingTargetForSelector:), and it'll finally, if everything else fails, it'll create an invocation (using methodSignatureForSelector: and punt to forwardInvocation:. By default, forwardInvocation: just calls doesNotRecognizeSelector: which crashes you on iOS (or terminates the current thread on OS X). But you can override it to do something else (like they have here).
methodSignatureForSelector: is necessary so that the runtime system can create an invocation out of a message. This one either returns a method signature from this object or its superclasses, or it asks one of its targets for the appropriate method signature. A selector by itself isn't enough to figure out exactly how to call a method. The system needs to ask an object how the method actually works (what types it takes and what type it returns).
The code you've posted is a multi-delegate trampoline. It will accept any selector that its targets respond to (technically it'll pick a random target and see if it responds), and it'll forward that message to all of its targets.
For a similar trampoline with some comments about usage, you may want to look at RNObserverManager.
Take a look at the link in bfitch's comment. That covers what NSInvocation is, and hints at why you would use it, but doesn't cover the why in much detail.
NSInvocation lets you do fairly advanced things like creating a proxy object that actually forwards messages to another object. With NSInvocation it's possible to take ANY message at runtime and forward it to another object.
Another example is the performSelector family of methods. There's performSelector:, performSelector:withObject:, and performSelector:withObject:withObject:. (plus variants like performSelector:withObject:afterDelay:, performSelector:onThread:, etc.) Those methods take 0, 1, or or 2 objects as parameters. If you need to invoke a method on another object that takes scalar parameters, or anything other than 0, 1 or 2 objects, you're out of luck. However you can send messages with ANY type of parameters using NSInvocation.
Note that when blocks were added to Objective-C the need for tricks like performSelector and NSInvocation become less. Blocks can reference variables from their enclosing scope, which makes them more flexible.
Let's say I have defined a protocol for a subclassed UIView as follows:
#protocol MyCustomViewDelegate <NSObject>
- (NSString*) titleForItemAtIndex;
- (UIImage*) imageForItemAtIndex;
#end
I would want the class implementing the delegate methods to implement only one, and not both the delegate methods. If the delegate implements titleForItemAtIndex, it must NOT implement imageForItemAtIndex, and vice versa. The compiler must throw a warning (or some other way to communicate to this to the programmer) if both methods are implemented by the delegate class. Is that possible?
You can ask if the the delegate instance responds to a specific selector:
if ([self.delegate respondToSelector:#selector(titleForItemAtIndex)]) {
NSString * title = [title titleForItemAtIndex];
}
else if ([self.delegate respondToSelector:#selector(imageForItemAtIndex)]) {
UIImage * title = [title imageForItemAtIndex];
}
This will also require that you mark your delegate methods as #optional in the protocol declaration. With this condition you guarantee that the first method has the precedence on the second.
You can add one more else and throw an exception if none of them is called.
I dont think its possible to throw compiler errors. But still exceptions can be raised at runtime. You can use NSAssert and make sure that only one method is implemented at run time. This does not throw a compiler error but causes the app to crash with a log saying that only one method should be implemented.
// Checks for titleForItemAtIndex
if ([self.delegate respondToSelector:#selector(titleForItemAtIndex)])
{
// Delegate has implemented titleForItemAtIndex.
// So it should not be implementing imageForItemAtIndex
// Below assert will check this
NSAssert(![self.delegate respondToSelector:#selector(imageForItemAtIndex)], #"Delegate must not respond to imageForItemAtIndex");
// Now that condition is checked. Do anything else as needed below.
}
// Checks for imageForItemAtIndex
if ([self.delegate respondToSelector:#selector(imageForItemAtIndex)]) {
// Delegate has implemented imageForItemAtIndex.
// So it should not be implementing titleForItemAtIndex
// Below assert will check this
NSAssert(![self.delegate respondToSelector:#selector(titleForItemAtIndex)], #"Delegate must not respond to titleForItemAtIndex");
// Now that condition is checked. Do anything else as needed below.
}
Another approach would be to create separate protocols for both methods, and use the same if assert condition but with conformsToProtocol If you have a lot of mutually exclusive methods, its better to create separate protocols.
I need to add additional behavior to methods I need to extend, i.e. implement method that looks like
- (void)extendMethod:(SEL)selector forClass:(Class)class withCompletionBlock:(void (^)(void))completionBlock;
So every time Class instance call a method with SEL selector in addition should be invoked my completion block.
I've tried method swizzling, but ran into some problems: I want original method implementation to be called.
What I need is very similar with subclassing, but this should be implemented without subclassing.
UPDATE:
For example I have subclass of UIViewController named MyViewController. MyViewController have - (void)viewDidLoad method. Somewhere in application I call method
[methodExtender extendMethod:#selector(viewDidLoad)
forClass:[MyViewController class]
withCompletionBlock:^{
NSLog(#"view did load called");
}];
So after viewDidLoad method of every instance of MyViewController my completion block invoked.
I'm not sure how you want to use selector, but you can try extend any class(even ones that you don't have implementation file) by using mechanism in Objective-C know as "Categories".
From Xcode click on File->New->File (command+n)
From Cocoa Touch choose Objective-C category
Type name of your category and choose class on which you want to make category (I choosed UIButton)
Then next and Create.
Xcode will create two files for example: UIButton+extendMethod.h
Declare your method in header file and implement it in *.m file.
Using
If you want to use in let's say your View Controller in *.h file import
#import "UIButton+extendMethod.h"
and then you can call your method like this:
UIButton *button = [[UIButton alloc] init];
[button extendMethod:#selector(yourMethod:)];
Swizzling does allow you to call the original implementation, though it is just a bit confusing. Because the implementations are swapped after swizzling, you call the original implementation using the selector of the swizzled method:
- (void)mySwizzledImplementation {
// do stuff
// now call original implementation (using swizzled selector!)
[self mySwizzledImplementation];
// do more stuff
}
See also http://cocoadev.com/wiki/MethodSwizzling
There is no way (anymore)to simulate inheritance without subclassing. There use to be Posing, method swizzling is all that is left (not as elegant as posing though). Here is one way to do method swizzling correctly while being able to call the original implementation.
int swizzle_instance_methods(Class class, SEL selector, IMP replacement, IMP *store) {
#synchronized(class) {
Method method = class_getInstanceMethod(class, selector);
IMP original_imp = NULL;
if (method != NULL) {
const char *type = method_getTypeEncoding(method);
IMP original_imp = class_replaceMethod(class, selector, replacement, type);
if (original_imp == NULL)
original_imp = method_getImplementation(method);
if (original_imp != NULL && store != NULL) {
*store = original_imp;
}
}
return (original_imp != NULL);
}
}
+ (void) load
{
static IMP originalMethodImpl = NULL;
IMP customMethodImpl = imp_implementationWithBlock(^(id self_) {
NSString *descr = ((NSString(*)(id,SEL))originalMethodImpl)(self_, #selector(description);
return [NSString stringWithFormat:#"<--- %# --->",descr];
});
swizzle_instance_methods([self class], #selector(description), customMethodImpl, &originalMethodImpl);
}
I might add that this is nice for debugging and I think that it can be greate for building excellent frameworks. Alas, Apple seems to think differently and using method swizzling can result in your app being excluded from the App store. If you are not aiming for the app store then all the power to you.
It is be possible with ObjC categories. For ex, you can extend hasPrefix method of NSString as follows,
-(BOOL)hasPrefixx:(NSString *)aString
{
NSLog(#"Checking has prefix");
return [self hasPrefix:aString];
}
If you import the category, you should be able to call this method. But his means you got change the method in your call.
By the way, Method swizzling should work. Bit of explanation here.
I'm having problems to make the IOS (objective-c) KVO work for a key of type int.
My class declares a property sampleValue of type int. As int doesn't automatically implement the KVO functionality I've overrided the method automaticallyNotifiesObserversforKey as this:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
BOOL automatic = NO;
if ([theKey isEqualToString:#"sampleValue"]) {
automatic = NO;
} else {
automatic=[super automaticallyNotifiesObserversForKey:theKey];
}
return automatic;
}
The method is called just as I would expect is to be. I also have implemented a setter method for the sampleValue property like this:
- (void) setSampleValue:(int)newSampleValue
{
[self willChangeValueForKey:#"sampleValue"];
sampleValue = newSampleValue;
[self didChangeValueForKey:#"sampleValue"];
}
Setting up the observer in the observer class is done like this (dc is the instance of the observed object):
[dc addObserver:self forKeyPath:#"sampleValue" options:NSKeyValueObservingOptionNew context:NULL];
However, when the sampleValue is updated, no notification is sent to my observer object. Updating another property of type NSDate works absolutely fine.
Can anyone help me figure out what I'm doing wrong or what I should do to make this work.
Best regards
Tomo
Maybe I'm missing something in your question, but you can observe properties of type int just as easily as other types without doing anything special.
Try removing your +automaticallyNotifiesObserversForKey: override and your -setSampleValue: setter, and just synthesize the accessors for sampleValue:
#synthesize sampleValue;
int is the type of the value that corresponds to key #"sampleValue", but it's not the thing being observed. The object being observed is dc, and it'll take care of sending the proper notification when the sampleValue property is changed.