Objective-C Removing Inherited Methods from a class - objective-c

For a subclass, is there way I can remove/stop methods from super class implementation?
(ie. nsstring has a method length. i want to stop "mystring" class from attaining the length method).

length is a primitive method in the NSString class cluster; pretty much every other method in NSString is implemented using calls to length.
Instead of going down this path of madness, make your class a subclass of NSObject. Without a length method, it isn't really a valid NSString subclass anyway.

You can override the method to call doesNotRecognizeSelector::
- (NSUInteger)length
{
[self doesNotRecognizeSelector:_cmd];
}
But if you have to do this, you may want to rethink whether you really want to subclass in the first place. It may be better to use composition instead of inheritance if you only want to "inherit" specific behaviors/methods.

You override respondsToSelector: to return NO for the appropriate selectors and override the method to call doesNotRecognizeSelector:.

The better answer is "don't do that". Don't try removing methods, instead put an NSAssert(NO, #"don't call me") in your class's implementation, and never call it yourself.
If you must make a "thing that is like X but doesn't do Y", make a new class, and delegate behavior to an instance of the real X.

Can't you simply override the method ?

You can use doesNotRecognizeSelector:. From the documentation:
- (id)copy
{
[self doesNotRecognizeSelector:_cmd];
}
However, there's a good chance that this is the Wrong Thing To Do. The interface for a class is a contract; it states that an instance that is a kind of that class (which includes subclass instances) will accept that message. When your MyString is passed as an NSString and it doesn't accept the length message, it's violating the contract and will likely cause the program to crash, since no one would think to handle an NSInvalidArgumentException from invoking length on an NSString.

Related

Is it possible to initialize a property in a category before any category method is called?

Is it possible to initialize a property in a category?
For example, if I have a mutable array property called nameList in a class. Is it possible to have a category created for this class to add an object to the array property before any of the category method is called?
If I understand you correctly, and others are interpreting your question differently, what you have is:
A class with a property
A category on that class
And you want to call a particular method automatically before any category method is called on a given instance, that method would "initialise" the category methods by modifying the property.
In other words you want the equivalent of a subclass with its init method, but using a category.
If my understanding is correct then the answer is no, there is no such thing as a category initializer. So redesign your model not to require it, which may be to just use a subclass - as that provides the behaviour you are after.
The long answer is you could have all the category methods perform a check, say by examining the property you intend to change to see if you have. If examining the property won't determine if an object has been "category initialized" then you might use an associated object (look in Apple's runtime documentation), or some other method, to record that fact.
HTH
Addendum: An even longer/more complex solution...
GCC & Clang both support a function (not method) attribute constructor which marks a function to be called at load time, the function takes no parameters and returns nothing. So for example, assume you have a class Something and a category More, then in the category implementation file, typically called Something+More.m, you can write:
__attribute__((constructor)) static void initializeSomethingMore(void)
{
// do stuff
}
(The static stops the symbol initializeSomethingMore being globally visible, you neither want to pollute the global name space or have accidental calls to this function - so you hide it.)
This function will be called automatically, much like a the standard class + (void) initialize method. What you can then do using the Objective-C runtime functions is replace the designated initializer instance methods of the class Something with your own implementations. These should first call the original implementation and then an initialize your category before returning the object. In outline you define a method like:
- (id) categoryVersionOfInit
{
self = [self categoryVersionOfInit]; // NOT a mistake, see text!
if (self)
{
// init category
}
return self;
}
and then in initializeSomethingMore switch the implementations of init and categoryVersionOfInit - so any call of init on an instance of Something actually calls categoryVersionOfInit. Now you see the reason for the apparently self-recursive call in categoryVersionOfInit - by the time it is called the implementations have been switched so the call invokes the original implementation of init... (If you're crosseyed at this point just draw a picture!)
Using this technique you can "inject" category initialization code into a class. Note that the exact point at which your initializeSomethingMore function is called is not defined, so for example you cannot assume it will be called before or after any methods your target class uses for initialization (+ initialize, + load or its own constructor functions).
Sure, it possible through objc/runtime and objc_getAssociatedObject/objc_setAssociatedObject
check this answer
No it's not possible in objective c.Category is the way to add only method to an existing class you can not add properties in to this.
Read this
Why can't I #synthesize accessors in a category?

Removing an action from a subclass

My NSDocument subclass implements selectAll:. Only problem is, I'm using NSTableView, and it also implements selectAll:. However, the selectAll: action in NSTableView doesn't do what I want, and it does prevent the selectAll: method in my Document class from ever being reached in the responder chain.
I already have a subclass of NSTableView, and after poking around a bit I got things working the way I want by adding a respondsToSelector: method to my NSTableView subclass which lies to the runtime by telling it there is no selectAll: action:
-(BOOL)respondsToSelector:(SEL)targetSelector
{
if (targetSelector == #selector(selectAll:)) {
return FALSE; // we don't want tableView's implementation of selectAll
}
return [super respondsToSelector:targetSelector];
}
This seems to work fine, allowing the selectAll: method in my document subclass to do its thing. But this solution leaves me a bit uneasy. What about other action methods I have implemented in this subclass? Do I need to manually check and return true for each of them? I do have two actions defined in this subclass, moveLeft: and moveRight:, and they seem to work, even though I am not handling them in respondsToSelector:. So my question is, am I doing this correctly, or is there something I am missing? Or perhaps there is some entirely different way to do this properly?
By the way, I got the idea of overriding respondsToSelector from this post on the OmniGroup forum:
http://mac-os-x.10953.n7.nabble.com/Removing-an-action-from-a-subclass-td27045.html
Sending a message to super affects which implementation of that method we use. It doesn't change who self is.
So let's try to imagine how respondsToSelector: works. Given a selector mySelector, it probably introspects every class up the superclass chain, starting with [self class], to see whether it actually implements mySelector.
Now then, let's say your subclass is called MyTableView. When MyTableView says
[super respondsToSelector:targetSelector]
what happens? The runtime will look up the superclass chain for another implementation of respondsToSelector:, and eventually will find NSObject's original implementation. What does that implementation do? Well, we just answered that: it starts the search for an implementation of targetSelector in [self class]. That's still the MyTableView class! So if you have defined moveLeft: in MyTableView, respondsToSelector: will find it and will return YES for moveLeft:, exactly as you hope and expect.
Thus, to generalize, the only selector for which this search has been perverted is the search for selectAll: - exactly as you hope and expect. So I think you can relax and believe that what you're doing is not only acceptable and workable but the normal solution to the problem you originally posed.
You might also like to look at the Message Forwarding chapter of Apple's Objective-C Runtime Programming Guide.

Creating and using a dummy NSData subclass doesn't work

I have a problem with creating my own subclass of NSData, which I want to have a custom description method. Even creating a dummy NSData subclass:
#interface MyData : NSData {}
#end
and
#implementation MyData
#end
and using it results in weird bugs (the function that uses it never exits, and control somehow returns to the run loop). I thought that maybe I am responsible for rewriting the designated initializers of NSData (calling the super implementation), but none is mentioned in the doc. So:
what are the designated initializers of NSData?
what is the bare minimum I need to write for a dummy subclass of NSData?
Making an NSData subclass is difficult because (as drewag noted) it is a member of a class cluster. From the Binary Data Programming Guide:
...data objects are not actual instances of the NSData or NSMutableData classes but instead are instances of one of their private subclasses.
When you do [[NSData alloc] initWith...] you don't get back an NSData; you probably get back an NSConcreteData. The extraordinary Cocoa With Love has a discussion and demonstration of subclassing class clusters.
The best (and most idiomatic) option is probably composition: your custom class should simply contain an NSData ivar, and implement a description method that operates on that enclosed object.
While drewag's response is technically correct, this is a dangerous technique to use on Cocoa classes; it will override the description method of every NSData object in the program, whether you create it directly or not.
In the specific case of the description method this may be okay, but for another method more likely to be relied upon by other objects in the framework, it could cause large, hard-to-trace problems. You should only do this if you are sure that there is no other way.
It would be far better to create a category and method with a prefix:
#interface NSData (FX_Description)
- (NSString *)FX_description;
#end
The Apple docs specifically mention this category-override technique and advise against it:
Because the methods declared in a category are added to an existing class, you need to be very careful about method names.
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.
An earlier version of the docs went on to say:
The very presence of some category methods may cause behavior changes across all frameworks. For example, if you override the windowWillClose: delegate method in a category on NSObject, all window delegates in your program then respond using the category method; the behavior of all your instances of NSWindow may change. Categories you add on a framework class may cause mysterious changes in behavior and lead to crashes. [Emphasis mine.]
If all you want is to override a single function "description" consider using a "Category" instead:
#interface NSData (MyData)
-(NSString*)description;
#end
#implimentation NSData (MyData)
-(NSString*)description
{
return #"something";
}
#end
Then, you can use this function on any instance of NSData.
It is very difficult to subclass NSData because it is a "Class Cluster." The public API treats it as one class, but in reality it is a collection of hidden subclasses. You can research overriding a class cluster, but it is almost never needed. Another option is to create your "MyData" class with NSData as a member variable instead of using a subclass.

Intercept method call in Objective-C

Can I intercept a method call in Objective-C? How?
Edit:
Mark Powell's answer gave me a partial solution, the -forwardInvocation method.
But the documentation states that -forwardInvocation is only called when an object is sent a message for which it has no corresponding method. I'd like a method to be called under all circumstances, even if the receiver does have that selector.
You do it by swizzling the method call. Assuming you want to grab all releases to NSTableView:
static IMP gOriginalRelease = nil;
static void newNSTableViewRelease(id self, SEL releaseSelector, ...) {
NSLog(#"Release called on an NSTableView");
gOriginalRelease(self, releaseSelector);
}
//Then somewhere do this:
gOriginalRelease = class_replaceMethod([NSTableView class], #selector(release), newNSTableViewRelease, "v#:");
You can get more details in the Objective C runtime documentation.
Intercepting method calls in Objective-C (asuming it is an Objective-C, not a C call) is done with a technique called method swizzling.
You can find an introduction on how to implement that here. For an example how method swizzling is implemented in a real project check out OCMock (an Isolation Framework for Objective-C).
Sending a message in Objective-C is translated into a call of the function objc_msgSend(receiver, selector, arguments) or one of its variants objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret.
If it was possible to change the implementation of these functions, we could intercept any message. Unfortunately, objc_msgSend is part of the Objective-C runtime and cannot be overridden.
By googling I found a paper on Google Books: A Reflective Architecture for Process Control Applications by Charlotte Pii Lunau. The paper introduces a hack by redirecting an object's isa class pointer to an instance of a custom MetaObject class. Messages that were intended for the modified object are thus sent to the MetaObject instance. Since the MetaObject class has no methods of its own, it can then respond to the forward invocation by forwarding the message to the modified object.
The paper does not include the interesting bits of the source code and I have no idea if such an approach would have side effects in Cocoa. But it might be interesting to try.
If you want to log message sends from your application code, the -forwardingTargetForSelector: tip is part of the solution.
Wrap your object:
#interface Interceptor : NSObject
#property (nonatomic, retain) id interceptedTarget;
#end
#implementation Interceptor
#synthesize interceptedTarget=_interceptedTarget;
- (void)dealloc {
[_interceptedTarget release];
[super dealloc];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
NSLog(#"Intercepting %#", NSStringFromSelector(aSelector));
return self.interceptedTarget;
}
#end
Now do something like this:
Interceptor *i = [[[Interceptor alloc] init] autorelease];
NSFetchedResultsController *controller = [self setupFetchedResultsController];
i.interceptedTarget = controller;
controller = (NSFetchedResultsController *)i;
and you will have a log of message sends. Note, sends sent from within the intercepted object won't be intercepted, as they will be sent using the original object 'self' pointer.
If you only want to log messages called from the outside (usually called from delegates; to see which kind of messages, when, etc.), you can override respondsToSelector like this:
- (BOOL)respondsToSelector:(SEL)aSelector {
NSLog(#"respondsToSelector called for '%#'", NSStringFromSelector(aSelector));
// look up, if a method is implemented
if([[self class] instancesRespondToSelector:aSelector]) return YES;
return NO;
}
Create a subclass of NSProxy and implement -forwardInvocation: and -methodSignatureForSelector: (or -forwardingTargetForSelector:, if you're simply directing it on to a second object instead of fiddling with the method yourself).
NSProxy is a class designed for implementing -forwardInvocation: on. It has a few methods, but mostly you don't want them to be caught. For example, catching the reference counting methods would prevent the proxy from being deallocated except under garbage collection. But if there are specific methods on NSProxy that you absolutely need to forward, you can override that method specifically and call -forwardInvocation: manually. Do note that simply because a method is listed under the NSProxy documentation does not mean that NSProxy implements it, merely that it is expected that all proxied objects have it.
If this won't work for you, provide additional details about your situation.
Perhaps you want NSObject's -forwardInvocation method. This allows you to capture a message, retarget it and then resend it.
You can swizzle the method call with one of your own, which does whatever you want to do on "interception" and calls through to the original implementation. Swizzling is done with class_replaceMethod().
A method call, no. A message send, yes, but you're going to have to be a lot more descriptive if you want a good answer as to how.
To do something when a method is called, you could try an events based approach. So when the method is called, it broadcasts an event, which is picked up by any listeners. I'm not great with objective C, but I just figured out something similar using NSNotificationCenter in Cocoa.
But if by "intercept" you mean "stop", then maybe you need more logic to decide wether the method should be called at all.

how to block a superclass method to be called to a subclass

I'm extending the functionality of a class with a subclass, and I'm doing some dirty stuff that make superclass methods dangerous (app will hang in a loop) in the context of the subclass. I know it's not a genius idea, but I'm going for the low-hanging fruit, right now it's gonna save me some time. Oh it's a dirty job, but someone's gotta do it.
Bottom line, I need to either block that method from outside, or throw an exception when it's called directly to the superclass. (But I still use it from the subclass, except with care).
What would be the best way to do this?
UPDATE ---
So this is what I went for. I'm not self-answering, as Boaz' answer mentions multiple valid ways to do this, this is just the way that suited me. In the subclass, I overrode the method like this:
- (int)dangerousMethod
{
[NSException raise:#"Danger!" format:#"Do not call send this method directly to this subclass"];
return nil;
}
I'm marking this as answered, but evidently that doesn't mean it's closed, further suggestions are welcome.
Just re-implement the unsafe method in your subclass and have it do nothing or throw an exception or re-implement it as safe, just as long as the new implementation doesn't call the unsafe superclass method.
For the C++ crew in here: Objective C doesn't let you mark methods as private. You can use its category system to split up the interface into separate files (thus hiding 'private' ones), but all methods on a class are public.
You can override whichever methods you want to block in your subclass's .h file. You can make dangerousMethod unavailable by placing the following in your .h file.
- (int)dangerousMethod __attribute__((unavailable("message")));
This will make the dangerousMethod method unavailable to anyone using your subclass.
To keep other code from using the superclass's version this method, you can further restrict by putting this in your .m file.
- (int)dangerousMethod
{
return nil;
}
note: I'm ObjC/Cocoa newcomer:
#implementation MyClass
-(void)myMethod:(NSString *)txt{
if([self class] != [MyClass class]) return;
NSLog(#"Hello");
}
#end
Peter
This article explains how to create private variables in Objective C. They aren't truly private, but from what I read, the compiler will throw a warning if you try to call them from the subclass.
If you create the methods in your superclass as "private" then the subclass has no possible way of calling them. I'm not familiar with Objective C, but every other object oriented language I've seen has the "private" qualifier.