Swizzling and super - ios

I am trying to swizzle the canPerformAction:withSender: method for UIResponder and all its subclasses which have overridden this method.
I am doing this by storing the original implementations in a dictionary keyed by class name; and looking up the dictionary in the swizzled version of the implementation before calling out to the original implementation.
This seems to work fine for some cases, but fails when the original implementation calls out to super. Then my swizzled method continuously keeps getting invoked and the program gets into infinite recursion.
What could be wrong here?

After the swizzle the -original with -custom:
-(void)custom {
[self custom]; // calls -original
}
-(void)original {
[self original]; // calls -custom
}
Said that, if you have the methods swizzled in the superclass, objc_msgSendSuper will do the same: call original for custom and versa giving you the recursion.
-(void)custom {
[self original]; // calls -custom, makes recursion
}
-(void)original {
[self custom]; // calls -original, makes recursion
}

Related

respondsToSelector for super class invocation

I have the following method:
- (void) someMethod
{
if ([super respondsToSelector:#selector(someMethod)])
{
[super performSelector:#selector(someMethod)
withObject:nil];
}
}
someMethod does not exist on superclass. as i understand, if there is no such method, runtime will ask the next responder in chain for such method till the NSObject class. And i was sure, that if statement will return NO.
Statement return YES. After that it performs selector without crash. As result - infinite recursion.
so, i have two questions:
Why [super respondsToSelector:#selector(someMethod)] returns YES ?
Why [super performSelector:#selector(someMethod) withObject:nil] does not crash with error 'does not responds to selector' ?
I think i've missed something essential.
Please, help.
Yes, you missed something essential as you suggest. From the documentation for respondsToSelector:
You cannot test whether an object inherits a method from its superclass by sending respondsToSelector: to the object using the super keyword. This method will still be testing the object as a whole, not just the superclass’s implementation. Therefore, sending respondsToSelector: to super is equivalent to sending it to self. Instead, you must invoke the NSObject class method instancesRespondToSelector: directly on the object’s superclass, as illustrated in the following code fragment.
if( [MySuperclass instancesRespondToSelector:#selector(aMethod)] )
{
// invoke the inherited method
[super aMethod];
}
HTH

Make two Protocol methods exclusive (implement one or the other, not both)

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.

Assign code blocks to a property objective c

I'm attempting to get Background App Refresh going in my iOS application. However, I'm having some trouble understanding code blocks.
I've done some research on it, and would say I have a beginner's understanding so far. The method in question is:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
This method wants a UIBackgroundFetchResult return type. Due to the complexity of my application though, I cannot return that with ease. There's a lot that happens when pulling data from the internet in Background mode.
In the body of that method, I have a custom method that also has a completion block. What I'm trying to do is define another custom method in my code that would be assigned to the completion handler.
In my data manager, I have a property defined as :
#property (copy, nonatomic) void (^fetchCompleted)(UIBackgroundFetchResult);
In the performFetchWtihCompletionHandler method implementation, I call on my data manager:
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
_fetchCompleted = completionHandler;
_state = DMSMT_WaitingForPartnerData;
[self getActiveQueues];
}
Once my downloads are completed, I call on the fetchCompleted method:
[self fetchCompleted];
Herein lies my problem. I need to pass a UIBackgroundFetchResult argument, but I see no way to do that. I tried [self fetchCompleted:UIBackgroundFetchResultNewData]; but it yells at me.
Any ideas?
Thanks in advance.
EDIT:
Here was the fix. So simple!
if(_fetchCompleted != nil){
[self fetchCompleted](UIBackgroundFetchResultNewData);
}
You are treating fetchCompleted as a method but it is a block! Try this out:
-(void)getActiveQueues {
// Do some work here
// When you are finished...
// Set the appropriate result
UIBackgroundFetchResult result;
// Check to make sure fetchCompleted is not NULL
if (self.fetchCompleted) {
// Invoke the block!
self.fetchCompleted(result);
}
}
This method wants a UIBackgroundFetchResult return type
No, it wants a void return type. One of the parameters is of type UIBackgroundFetchResult. Parameters are not return results. UIBackgroundFetchResult is just a type of variable.
Which appears to flow into your error. [self fetchCompleted] is the getter that will return the fetchCompleted variable. It doesn't do anything with it.
To perform a block, use function-like syntax. E.g. [self fetchCompleted]().

How to swizzle AudioSessionAddPropertyListener

There is a library in my project that is adding audio property listeners. I really need to be able to block it from doing so, but I don't have the source code.
I've done a swizzle before for the addObserver method in NSNotificationCenter. Could you help me do the same for AudioSessionAddPropertyListener? If the method trying to be added in the call does not match my whitelist, I want to block it. Otherwise, I'll call the original method.
I don't know what class for which I should overload the load function. I'm looking inside of
Audio.h. I'm adding some pseudo/real/badlyNamed code so you can see what I'm trying to do.
#import <AudioToolbox/AudioToolbox.h>
#interface AuidoClassUmm (SOMETHING)
#end
+ (void) load
{
Method original, swizzled;
original = class_getInstanceMethod(self, #selector(AudioSessionAddPropertyListener:selector:name:object:));
swizzled = class_getInstanceMethod(self, #selector(swizzled_AudioSessionAddPropertyListener:selector:name:object:));
method_exchangeImplementations(original, swizzled);
}
-(void) swizzled_AudioSessionAddPropertyListener:selector:name:object:
{
if(//funciton object I don't like)
{
return;
}
else
{
// Calls the original addObserver function
[self swizzled_AudioSessionAddPropertyListener::notificationObserver selector:notificationSelector name:notificationName object:notificationSender];
}
}
AudioSessionAddPropertyListener() is a C function, not an Objective-C method. You can't swizzle it.
(Even if you could swizzle it, that would almost certainly be a bad idea.)

How to communicate with the delegate in a custom class's init method in Objective-C

I spent much time to get a better understanding in delegation in Objective-C. I got it working for most cases, but there is a problem in a specific case, which I find difficult to understand. Let me explain what I am trying to do:
I have a custom view called GridLayoutView, which is subclass of UIView. I also have a view controller SomeViewController, which is the delegate of GridLayoutView.
I have a custom initWithFrame method, and I am conditionally calling another initialization method baseInit. That method calls a delegate method at some time. Here is some code from GridLayoutView:
//
// Delegator
// GridLayoutView.m
//
#implementation GridLayoutView
- (id)initWithFrame:(CGRect)frame
numberOfRows:(NSUInteger)rows
numberOfCols:(NSUInteger)cols
{
self = [super initWithFrame:frame];
if (self) {
self.numberOfRows = rows;
self.numberOfCols = cols;
self.numberOfCells = rows * cols;
if (self.numberOfCells > 0) [self baseInit];
}
return self;
}
- (void)baseInit
{
// do some more initialization stuff here
// ...
// then call a delegate method
[self.delegate someMethod:someObj];
// However, this method is not called because self.delegate is nil
}
and some code from SomeViewController:
//
// Delegate
// SomeViewController.m
//
#implementation SomeViewController
// ...
// in some method
self.gridLayoutView = [[GridLayoutView alloc] initWithFrame:gridLayoutFrame
numberOfRows:rowsCount
numberOfCols:colsCount];
self.gridLayoutView.delegate = self;
// ...
The delegate method never gets called within baseInit, because the delegate is nil at that time and it gets set after initWithFrame and baseInit methods are done. I have confirmed this.
I sense that there is something wrong in my workflow of delegation. I have a solution but I don't think it is the best way to go. The solution is basically passing the SomeViewController instance to the delegator by modifying the initWithFrame method such as:
- (id)initWithFrame:(CGRect)frame
numberOfRows:(NSUInteger)rows
numberOfCols:(NSUInteger)cols
delegate:(id<GridLayoutViewDelegate>)aDelegate
This approach works, but I am uncomfortable due to passing SomeViewController to GridLayoutView in its initWithRect. I am wondering if this is a good way to go with delegation or is there a better approach? I would be very grateful if someone can clear this for me.
If I'm understanding you correctly, there aren't many options here.
Modifying your initializer (as you suggested) to pass in the delegate. There is nothing wrong with that, don't know why you don't like it.
Remove the dependency on the delegate during initialization and instead, send whatever delegate message is appropriate when the delegate property is set by overriding the setter:
- (void)setDelegate:(id<GridLayoutViewDelegate>)aDelegate
{
_delegate = aDelegate;
// send whatever message makes sense to the delegate
[_delegate someMethod:object];
}
EDIT - noticed your comment
Your initialization method should not take any significant amount of time. It's unclear what you mean by 'loading views'. If you simply mean creating and adding subviews to a view then that is fast and there should be no need to communicate progress to a delegate (which you can't do anyway b/c the initialization is on the main thread and UI won't update until all of init is complete).
If you mean loading data that takes a long time, you should disconnect that from initialization and load the data in a background operation, sending progress messages to a delegate.
i would implement the setDelegate function and then call
[self someMethod:someObj]; from there

Resources