How to Only Override a Method Depending on the Runtime System iOS Version

How to only override a method depending on the runtime system iOS version?

One solution would be to override the respondsToSelector: method in your view controller. Have it return NO under iOS 8 when checking for the heightForRowAtIndexPath: method.

- (BOOL)respondsToSelector:(SEL)selector {
static BOOL useSelector;
static dispatch_once_t predicate = 0;
dispatch_once(&predicate, ^{
useSelector = [[UIDevice currentDevice].systemVersion floatValue] < 8.0 ? YES : NO;
});

if (selector == @selector(tableView:heightForRowAtIndexPath:)) {
return useSelector;
}

return [super respondsToSelector:selector];
}

This way, when the table view make a call like:

if ([self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) {
}

your code will return NO under iOS 8 or later and YES under iOS 7 or earlier.

Is overriding Objective-C framework methods ever a good idea?

If you're extending the question from mere swizzling to actual library modification then I can think of useful examples.

As of iOS 5, NSURLConnection provides sendAsynchronousRequest:queue:completionHandler:, which is a block (/closure) driven way to perform an asynchronous load from any resource identifiable with a URL (local or remote). It's a very useful way to be able to proceed as it makes your code cleaner and smaller than the classical delegate alternative and is much more likely to keep the related parts of your code close to one another.

That method isn't supplied in iOS 4. So what I've done in my project is that, when the application is launched (via a suitable + (void)load), I check whether the method is defined. If not I patch an implementation of it onto the class. Henceforth every other part of the program can be written to the iOS 5 specification without performing any sort of version or availability check exactly as if I was targeting iOS 5 only, except that it'll also run on iOS 4.

In Java or C++ I guess the same sort of thing would be achieved by creating your own class to issue URL connections that performs a runtime check each time it is called. That's a worse solution because it's more difficult to step back from. This way around if I decide one day to support iOS 5 only I simply delete the source file that adds my implementation of sendAsynchronousRequest:.... Nothing else changes.

As for method swizzling, the only times I see it suggested are where somebody wants to change the functionality of an existing class and doesn't have access to the code in which the class is created. So you're usually talking about trying to modify logically opaque code from the outside by making assumptions about its implementation. I wouldn't really support that as an idea on any language. I guess it gets recommended more in Objective-C because Apple are more prone to making things opaque (see, e.g. every app that wanted to show a customised camera view prior to iOS 3.1, every app that wanted to perform custom processing on camera input prior to iOS 4.0, etc), rather than because it's a good idea in Objective-C. It isn't.

EDIT: so, in further exposition — I can't post full code because I wrote it as part of my job, but I have a class named NSURLConnectionAsyncForiOS4 with an implementation of sendAsynchronousRequest:queue:completionHandler:. That implementation is actually quite trivial, just dispatching an operation to the nominated queue that does a synchronous load via the old sendSynchronousRequest:... interface and then posts the results from that on to the handler.

That class has a + (void)load, which is the class method you add to a class that will be issued immediately after that class has been loaded into memory, effectively as a global constructor for the metaclass and with all the usual caveats.

In my +load I use the Objective-C runtime directly via its C interface to check whether sendAsynchronousRequest:... is defined on NSURLConnection. If it isn't then I add my implementation to NSURLConnection, so from henceforth it is defined. This explicitly isn't swizzling — I'm not adjusting the existing implementation of anything, I'm just adding a user-supplied implementation of something if Apple's isn't available. Relevant runtime calls are objc_getClass, class_getClassMethod and class_addMethod.

In the rest of the code, whenever I want to perform an asynchronous URL connection I just write e.g.

[NSURLConnection sendAsynchronousRequest:request
queue:[self anyBackgroundOperationQueue]
completionHandler:
^(NSURLResponse *response, NSData *data, NSError *blockError)
{
if(blockError)
{
// oh dear; was it fatal?
}

if(data)
{
// hooray! You know, unless this was an HTTP request, in
// which case I should check the response code, etc.
}

/* etc */
}

So the rest of my code is just written to the iOS 5 API and neither knows nor cares that I have a shim somewhere else to provide that one microscopic part of the iOS 5 changes on iOS 4. And, as I say, when I stop supporting iOS 4 I'll just delete the shim from the project and all the rest of my code will continue not to know or to care.

I had similar code to supply an alternative partial implementation of NSJSONSerialization (which dynamically created a new class in the runtime and copied methods to it); the one adjustment you need to make is that references to NSJSONSerialization elsewhere will be resolved once at load time by the linker, which you don't really want. So I added a quick #define of NSJSONSerialization to NSClassFromString(@"NSJSONSerialization") in my precompiled header. Which is less functionally neat but a similar line of action in terms of finding a way to keep iOS 4 support for the time being while just writing the rest of the project to the iOS 5 standards.

Override a method in a single object instance

What you have there in Java is an anonymous subclass. This is not possible in Objective-C (well, it sort of is but you would have to do some pretty involved contortions with the Obj-C runtime library).

But Objective-C as of iOS 4 or OS X 10.6 has "blocks", which is what the ^{} syntax is for. This is Objective-C's notion of a closure. This isn't going to help you directly if the APIs that you're calling don't support block callbacks, but you may be able to create wrapper classes that use blocks instead of subclassed methods to handle callbacks.

There are many resources for learning about blocks in Objective-C.

Override a method in Objective c via category

Objective-C messaging is dynamic, this means that it doesn't matter if you import or not the category. The object will receive the message and execute that method.

The category is overriding your method. This means that when the runtime sends a message to that object, will always find the overridden method, no matter what you import.

If you want to ignore a category you shouldn't compile it, so you could remove the category from the compiler sources.

An alternative is subclassing.

Also read this:

Avoid Category Method Name Clashes

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. This is less likely to be an issue if you’re using categories with your own classes, but can cause problems when using categories to add methods to standard Cocoa or Cocoa Touch classes.

So in your case you haven't got any problem because as stated, this is less likely to happen with user defined classes. But you should definitely use subclassing instead of writing a category.

How to avoid superclass methods getting overridden by sub class in objective - c

You can't. Efficiency doesn't come into it. If you are that bothered about security don't use objective-c. There will always be a way to get around any measures you take.

Is it possible to have an Objective-C Category that is only loaded if the method doesn't already exist?

Not directly, no.

The way I'd recommend doing this would be to have a my_sortDescriptorWithKey: which can then check if the class responds to sortDescriptorWithKey: and uses that if it does, otherwise use your own implementation.

Is it possible to overwrite a category method in a category of the subclass?

From "Customizing Existing Classes"
in the "Programming with Objective-C" guide:

At runtime, there’s no difference between a method added by a category
and one that is implemented by the original class.

So the answer to your question is YES. There is no difference between overriding a method
in the subclass itself or in a category of the subclass. It makes also no difference if
the overridden method is implemented in the base class itself or in a category of the base class.

The only thing you cannot do is to use a category to override a method of the same class or of another category of the class.

Overriding methods in Swift extensions

Extensions cannot/should not override.

It is not possible to override functionality (like properties or methods) in extensions as documented in Apple's Swift Guide.

Extensions can add new functionality to a type, but they cannot override existing functionality.

Swift Developer Guide

The compiler is allowing you to override in the extension for compatibility with Objective-C. But it's actually violating the language directive.

That just reminded me of Isaac Asimov's "Three Laws of Robotics" /p>

Extensions (syntactic sugar) define independent methods that receive their own arguments. The function that is called for i.e. layoutSubviews depends on the context the compiler knows about when the code is compiled. UIView inherits from UIResponder which inherits from NSObject so the override in the extension is permitted but should not be.

So there's nothing wrong with grouping but you should override in the class not in the extension.

Directive Notes

You can only override a superclass method i.e. load() initialize()in an extension of a subclass if the method is Objective-C compatible.

Therefore we can take a look at why it is allowing you to compile using layoutSubviews.

All Swift apps execute inside the Objective-C runtime except for when using pure Swift-only frameworks which allow for a Swift-only runtime.

As we found out the Objective-C runtime generally calls two class main methods load() and initialize() automatically when initializing classes in your app’s processes.

Regarding the dynamic modifier

From the Apple Developer Library (archive.org)

You can use the dynamic modifier to require that access to members be dynamically dispatched through the Objective-C runtime.

When Swift APIs are imported by the Objective-C runtime, there are no guarantees of dynamic dispatch for properties, methods, subscripts, or initializers. The Swift compiler may still devirtualize or inline member access to optimize the performance of your code, bypassing the Objective-C runtime. /p>

So dynamic can be applied to your layoutSubviews -> UIView Class since it’s represented by Objective-C and access to that member is always used using the Objective-C runtime.

That's why the compiler allowing you to use override and dynamic.



Related Topics



Leave a reply



Submit