How to override method of - ios

There is a class alpha which has method -(void)doSomething. There is also a class beta which I've created and can modify as I need. Instance of alpha can only be created by calling [gamma createAlpha].
What I need is to make class beta to inherit alpha and override doSomething while I still have to create instances of alpha via calling [gamma createAlpha].
Is there any way to implement this?
I hope my question makes sense.
Thank you!

Yes. You can use method swizzling. You will need to create category with following code:
#implementation alpha (newDoSomething)
// This is called when the category is being added
+ (void) load {
Method method1 = class_getInstanceMethod(self, #selector(doSomething));
Method method2 = class_getInstanceMethod(self, #selector(swizzleddoSomething));
// this is what switches the two methods
method_exchangeImplementations(method1, method2);
}
- (void)swizzleddoSomething
{
... your code
//You can call super implementation here with following line
//[self swizzleddoSomething];
}
#end
You will still have class alpha. It will be successfully created by [gamma createAlpha], but it will have your implementation of doSomething method.

Related

Replace method implementation in Swift

I'm trying to replace method implementation with parent implementation:
// Get superclass's valueDisplayText()
// XLFormSelectorCell is a parent of HCFormSelectorCell
// HCFormSelectorCell - swift class
// XLFormSelectorCell - obj-c class
let impParent = class_getMethodImplementation(XLFormSelectorCell.self, Selector("valueDisplayText"))
let method = class_getInstanceMethod(HCFormSelectorCell.self, Selector("valueDisplayText"))
method_setImplementation(method, impParent)
class_replaceMethod(HCFormSelectorCell.self, Selector("valueDisplayText"), impParent, method_getTypeEncoding(method))
But instead in replaces parent implementation with subclass implementation.
Why and how to achieve desired?
P.S: The reason I'm doing this: I need to call parent class's hidden method (obj-c class). Any other approaches to do this?

How should CategoryA override categoryB method

In AFNetworking 2.0, in UIImageView+AFNetworking there is a method:
+ (id <AFImageCache>)sharedImageCache
I want to override it and return here my custom object. I'd like also to override all the methods in AFImageCache, so basically I'd make a new protocol here. I've thought about method swizzling, however because lack of experience I'm not sure if it works ok with 2 categories. What if my category loads before AFNetworking category, will it still be working?
At all, is this approach a good one? I want to add disc caching to the memory one and I wonder which way is the cleanest one in terms of code quality.
Do not use Categories to override a method. Per the documentation
"If the name of a method declared in a category is the same as a
method in the original class, or a method in another category on
the same class (or even a superclass), the behavior is undefined
as to which method implementation is used at runtime. "
refer to the documentation at "Avoid Category Method Name Clashes" -->
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ProgrammingWithObjectiveC.pdf
Subclass and override the method instead and use the subclass?
Analyzing your scenario, It make sense to do method swizzling.
Note: Make sure yourCache will behave the same way as sharedImageCache, else it will result in crash.
#implementation UIImageView (Swizzling)
+ (void)load {
static dispatch_once_t token;
dispatch_once(&token, ^{
Class myClass = [self class];
Method originalMethod = class_getInstanceMethod(myClass, #selector(sharedImageCache));
Method newMethod = class_getInstanceMethod(myClass, #selector(NewSharedImageCache:));
method_exchangeImplementations(originalMethod, newMethod);
});
}
//This is just for sample. you can create your buffer in your own way
static id <yourCache> yourBuffer;
+ (id <yourCache>)NewSharedImageCache
{
return yourBuffer;
}
#end

How to forbid to inherit init method?

I have been struggling to find a correct and explicit answer, so I have decided to ask it here.
I create class A and define init method there:
#interface A : NSObject
- (id)initWithHealth:(int)hp;
#end
Then I am creating class B, that inherits from class A and define another init method there:
#interface B : A
- (id)initWithHealth:(int)hp andDamage:(int)dmg;
#end
In main, when I am going to instantiate an object from class B, I will be suggested by Xcode to use either - (id)initWithHealth:(int)hp; OR - (id)initWithHealth:(int)hp andDamage:(int)dmg; init method.
How can I forbid for class B to inherit init method from class A? I want my class B to have only one init method that I define. Is there a way to achieve this?
Thanks in advance.
You have a few choices:
Option 1 - supply a default "damage" with the old init method. In class B you would add:
- (id)initWithHealth:(int)hp {
return [self initWithHealth:hp andDamage:0]; // use an appropriate default
}
- (id)initWithHealth:(int)hp andDamage:(int)dmg {
self = [super initWithHealth:hp];
if (self) {
// do stuff with dmg
return self;
}
Option 2 - cause a runtime error if the old init method is used. In class B you would add:
- (id)initWithHealth:(int)hp {
NSAssert(0, #"Dont use this");
return nil; // make compiler happy
}
Personally I think Option 1 is the better choice.
In your subclass B you can explicitly define the method such that the class will not respond to it at runtime.
- (instancetype) initWithHealth:(int) hp {
[self release];
[super doesNotRecognizeSelector:_cmd];
return nil;
}
This is very conventional, I've seen it used on a lot of open source projects and on some of my client's projects. I think this is preferable to #rmaddy's solution when you want to ensure the inherited method is NEVER called. If you want a sane default when the inherited init method is called, then the other code works just fine.
EDITED (thanks to Sulthan and rmaddy)
OK I have found the answer to my question, I should have used categories.
But thanks for everyone who participated.

Passing class Object refrence to another class then trying to use it is not working

I am trying to pass a reference of my current NSObject Class through two other object classes so I can access the current initialization of the original NSObject class I called from.
I will try to outline why I am doing this in as simply as possible. I have 3 NSObject Classes and an appDelegate.
AppDelegate
RemoteSites
EngineRequest
EngineReasponse
This is the logical flow of the app as it stands
Appdelegate.m
calls RemoteSites method "GetRemoteSites" this method reutrns a BOOL for confirmation
RemoteSites.m
-(BOOL)GetRemoteSites {
// calls EngineRequests method like so
EngineRequests *engineRequests = [[EngineRequests alloc] init];
[engineRequests GetRemoteSites:self];
//..
}
EngineRequests.m
- (void)GetRemoteSites:(NSObject *)myObjectClass {
// get everything ready to send off request
}
send off request then return recived data + NSObject refrence to EngineReasponse
EngineReasponse.m
- (void)GetRemoteSites:(NSData *)receivedData Object:(NSObject *)requestingClass
{
// pass requestingClass to a NSObject var that will later be used to pass the data back to the original class that started the request
requestingClassObject = requestingClass
}
//..
[requestingClassObject GetRemoteSitesNow:reducedDataPacket]; // GetremoteSitesNow is a method inside RemoteSites class, however using requestingClassObject I cannot see any of the classMethods my class has in it
//..
So thats the overall flow of the process I am trying to complete, the whole point is to try and get -(BOOL)GetRemoteSites to return Yes to the AppDelegate.
In summery my question stands as this. Why can I not access RemoteSites methods from EngineReasponse's, I have passed the class Object refrence correctly I think but for some reason I cannot access the methods.
Any help solving my issue would be greatly appreciated.
EngineRequests.m
- (void)GetRemoteSites:(id)remoteSites {
// create your class object here or globally.
RemoteSites *remotesite = (RemoteSites*)remoteSites
}
EngineReasponse.m
- (void)GetRemoteSites:(NSData *)receivedData Object:(id)requestingClass
{
RemoteSites *requestingClassObject = (RemoteSites*)requestingClass
}
//
[requestingClassObject GetRemoteSitesNow:reducedDataPacket];
//
Sorry for the typo. Hope it will help.

How to explicitely pass self as a super class to a method and being called back instead of subclass?

I have 3 classes
One base class : MyBaseClass
That implements a method :
- (void) aSuperMethod {
[[UtilClass sharedUtil] doThisAndSendAnswerToMe:self]
}
- (void) answerReader:(bla bla)someParams {}
One subclass of MyBaseClass : MySubClass
That also implements these kind of methods :
- (void) anotherMethod {
[[UtilClass sharedUtil] doThisAndSendAnswerToMe:self]
[self aSuperMethod];
}
- (void) answerReader:(bla bla)someParams {}
And of course a utility class : UtilClass
That implements this kind of method :
- (void) doThisAndSendAnswerToMe:(id)listener {
do some stuff
[listener answerReader:someAnswerParams];
}
In debug mode, of course, the listener received in both calls by the doThisAndSendAnswerToMe method is of MySubClass class.
How may I do to point either on MySubClass or MyBaseClass depending on the call origin ?
You can't. The subclass's method implementation always overrides the superclass implementation.
One solution is to use a different method name in the superclass and the subclass, and pass the name of the callback method to UtilClass. UtilClass then uses -[NSObject performSelector:withObject:] to call that method.
-(void)baseClassAnswerReader:(id)someAnswerParams { ... }
[[UtilClass sharedUtil] doSomethingAndSendAnswerToMe:self
selector:#selector(baseClassAnswerReader:)];
Another solution is to pass a completion block object to UtilClass. UtilClass then calls the block object when it is done.
[[UtilClass sharedUtil] doSomethingWithCompletionBlock:^(id someAnswerParams){ ... }];
You use an instance of the base class if you want to use the base class, and you use an instance of the subclass if you want to use the subclass.
If you don't want the subclass method to override the superclass one, then make it a different method instead of overriding.

Resources