@Objc Keyword Extension Subclass Behaviour

@objc keyword extension subclass behaviour

Does @objc keyword enable message dispatch as well as dynamic?

Not usually. Usually, the @objc attribute on its own just exposes a given class member to Objective-C – Swift is still free to dispatch to it either using table or static dispatch. You would need to mark the member as dynamic if you wanted Swift to use message dispatch when calling it.

However, for a non-final @objc class extension member, Swift will automatically infer it to be dynamic. Why? Because for interoperability reasons, Swift allows @objc extension members to override and be overridden (much like how you can override an Obj-C method in a subclass category). In order to achieve this behaviour, Swift relies on Obj-C message dispatch.

Therefore, in an extension, @objc infers dynamic. You cannot override an extension member without exposing it to the Obj-C runtime because extension members cannot currently be added to Swift class vtables (as Swift vtables currently cannot have members dynamically added to them at runtime).

But this is not an NSObject instance.

On Apple platforms (i.e those with Obj-C interop), all Swift classes are exposed to the Obj-C runtime, and all implicitly inherit from a special Obj-C base class called _SwiftObject, which conforms to NSObjectProtocol. So Swift classes are able to take advantage of message dispatch without having to inherit from NSObject.

What's the mechanics behind extension's methods overriding with '@objc' attribute?

Extensions,

as the name already says, are supposed to extend/add/include methods
to an existing implementation, making them one of the most beautiful
things about Objective-C, and now Swift, since you can add code to a
class or framework you do not own. Therefore, it makes sense that
you’re not supposed to “replace” code in extensions, conceptually
speaking.

That’s why the compiler complains when you try to do it.

Also Check out this answer.

however this seems to be a support issue too, as swift compiler simply throw this error:

overriding non-@objc declarations from extensions is not supported.

According to Apple,

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

But that is not the case, as we are overriding from the extension not vice versa,
which takes us back to the declaration of extension.

Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.) Here.

Going back to the legacy topic swift compiler vs Objc compiler,

Dynamic dispatch vs. Static dispatch .

And there is no official documentation from apple on why this is not supported from the swift compiler or if they have any future plans to fix this or consider it an issue at all.

However, there’s no such thing as Swift dynamic dispatch; we only have
the Objective-C runtime’s dynamic dispatch. That means you can’t have
just dynamic and you must write @objc dynamic. So this is effectively
the same situation as before, just made explicit.

And here is a great article talking about this topic deeply.

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.

Whose witness table should be used?

That blog post is a bit outdated in that inheriting from NSObject no longer changes the dispatch behaviour of a class (in Swift 3, it would cause members to be implicitly exposed to Obj-C, which would change dispatch behaviour of extension members, but this is no longer the case). The example they give also no longer compiles in Swift 5, as you can only override a dynamic member from an extension.

In order to distinguish static dispatch from dynamic dispatch, let's consider protocols separately. For protocols, dynamic dispatch is used if both:

  • The member is declared within the main protocol body (this is called a requirement or customisation point).
  • The member is called on a protocol-typed value P, protocol composition typed value P & X, or generic placeholder typed value T : P, for example:

    protocol P {
    func foo()
    }

    struct S : P {
    func foo() {}
    }

    func bar(_ x: S) {
    x.foo() // Statically dispatched.
    }

    func baz(_ x: P) {
    x.foo() // Dynamically dispatched.
    }

    func qux<T : P>(_ x: T) {
    x.foo() // Also dynamically dispatched.
    }

If the protocol is @objc, message dispatch is used, otherwise table dispatch is used.

For non-protocol members, you can ask the question: "Can this be overriden?". If the answer is no, you're looking at static dispatch (e.g a struct member or final class member). If it can be overriden, you're looking at some form of dynamic dispatch. However it's worth noting that if the optimiser can prove that it isn't overriden (e.g if it's fileprivate and not overriden within that file), then it can be optimised to use static dispatch.

For a normal method call, the dynamic modifier is what distinguishes the two current forms of dynamic dispatch, table dispatch and Obj-C message dispatch. If a member is dynamic, Swift will use message dispatch. As stated, this rule seems pretty straightforward, however some members are not explicitly marked dynamic – the compiler infers it instead. This includes:

  • Members imported from Objective-C.
  • Overrides of dynamic members.
  • NSManaged properties.
  • Non-final @objc class extension members.

A lesser known form of method call in Swift is the dynamic method call, which is done by accessing an @objc member on an AnyObject value. For example:

import Foundation

class C {
@objc func foo() {}
}

func bar(_ x: AnyObject) {
// Message dispatch (crashing if the object doesn't respond to foo:).
x.foo!()
}

Such calls always use message dispatch.

And I think that about summarises the current rules around which dispatch mechanisms are used where.


Once we have subclass MisunderstoodPerson and pass this instance to
greetings(person:) this instance should be treated as Person. I have a
confusion here and have couple of questions here.

  • The instance is of type MisunderstoodPerson so would witness table of
    MisunderstoodPerson be used here.
  • Or the instance has been typecast to Person, so would witness table of Person be used here.

(slight terminology nitpick: for classes, it's called a vtable rather than a witness table)

It's always the vtable that corresponds to the dynamic type of the instance that's used, so in this case it would be MisunderstoodPerson's vtable.

Exposing an instance method to Objective-C within an extension method

I would take a different approach there extending UIViewController as follow:

protocol KeyboardController {
func keyboardWillShow(_ sender: Notification)
func keyboardWillHide(_ sender: Notification)
}

extension UIViewController: KeyboardController {
func addObservers() {
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillShow(_:)),
name:.UIKeyboardWillShow,
object: view.window)
NotificationCenter.default.addObserver(self,
selector: #selector(keyboardWillHide(_:)),
name:.UIKeyboardWillHide,
object: view.window)
}

func removeObservers() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: view.window)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: view.window)
}

func keyboardWillHide(_ notification: Notification) {
print("---> keyboardWillHide")

guard let userInfo = notification.userInfo else { return }
let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
print("keyboardSize", keyboardSize)

}

func keyboardWillShow(_ notification: Notification) {
print("---> keyboardWillShow")
guard

let userInfo = notification.userInfo,
let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size,
let offset = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size
else { return }

if keyboardSize.height == offset.height {
if self.view.frame.origin.y == 0 {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardSize.height
})
}
} else {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardSize.height - offset.height
})
}
}

}

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
addObservers()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Another option and the one I like most is to subclass UIViewController:

class KeyboardViewController: UIViewController {

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addObservers()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObservers()
}

func addObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: .UIKeyboardWillShow, object: view.window)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: view.window)
}

func removeObservers() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: view.window)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: view.window)
}

func keyboardWillHide(_ notification: Notification) {
print("---> keyboardWillHide")
if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size.height {
print("keyboardHeight", keyboardHeight)
view.frame.origin.y += keyboardHeight
}
}

func keyboardWillShow(_ notification: Notification) {
print("---> keyboardWillShow")
if let userInfo = notification.userInfo,
let keyboardHeight = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size.height,
let offsetHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.size.height {
print("userInfo", userInfo)
print("keyboardHeight", keyboardHeight)
print("offsetHeight", offsetHeight)
if keyboardHeight == offsetHeight {
if self.view.frame.origin.y == 0 {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y -= keyboardHeight
})
}
} else {
UIView.animate(withDuration: 0.1, animations: { () -> Void in
self.view.frame.origin.y += keyboardHeight - offsetHeight
})
}
}
}
}

class ViewController:  KeyboardViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

How to make an objective-c method private

One fairly solid solution to this is to use a static c method to do your initialization. All valid c is valid objective-c after-all.

So for example, CustomSuperclass.h:

#import <Foundation/Foundation.h>
@interface CustomSuperclass : NSObject
@property (nonatomic) NSUInteger integer;
@end

CustomSuperclass.m:

#import "CustomSuperclass.h"
@implementation CustomSuperclass
@synthesize integer = _integer;
static void initializeInstance(CustomSuperclass *self) {
self.integer = 9;
}
-(id)init{
if ((self = [super init])){
initializeInstance(self);
}
return self;
}
@end

CustomSubclass.h:

#import "CustomSuperclass.h"
@interface CustomSubclass : CustomSuperclass
@end

CustomSubclass.m:

#import "CustomSubclass.h"
@implementation CustomSubclass
// Obviously we would normally use (CustomSubclass *self), but we're proving a point with identical method signatures.
void initializeInstance(CustomSuperclass *self) {
self.integer = 10;
}
-(id)init{
if ((self = [super init])){
// We won't call this classes `initializeInstance()` function.
// We want to see if this one overrides it's superclasses version.
}
return self;
}
@end

Now if you run code like this:

CustomSuperclass *instanceOfSuperclass = [[CustomSuperclass alloc] init];
NSLog(@"superclass integer:%i", instanceOfSuperclass.integer);

CustomSubclass *instanceOfSubclass = [[CustomSubclass alloc] init];
NSLog(@"subclass integer:%i", instanceOfSubclass.integer);

You will see:

superclass integer:9
subclass integer:9

If the subclass's version of initializeInstance() was the one that was used by the subclass (when calling [super init]) then the subclasses integer would be 10. This proves the initializeInstance() method of the superclass was not overridden (at least within the scope of the superclass) by the subclass's version.

Edit in response to comment:

I posted this answer with its solution, not because I think it is the best way to solve the problem, but because it meets the specification of the question. The question is essentially, "How do I force a dynamic language to act in a static way so that I don't have to follow normal naming/initialization convention?"

The best way of handling the situation is for your class to have a designated initializer that each of the initializers call. Like:

/* Designated initializer */
-(id)initWithFile:(NSString *)file andURL:(NSURL *)url{
if ((self = [super init])){
_url = url;
_file = file;
// Initialization of new instance code here.
}
return self;
}
-(id)initWithFile:(NSString *)file{
return [self initWithFile:file andURL:nil];
}
-(id)initWithURL:(NSURL *)url{
return [self initWithFile:nil andURL:url];
}

Designated initializers work for almost all cases. They're a bit cumbersome for UIView subclasses, since an IB UIView will have initWithCoder: called where a code instantiated UIView will/should have initWithFrame: called. This is a situation in which a common method might be written to finish up after the calls to [super initWith...] to reduce code duplication.

One could run into the problem you are having with overriding the common initialization method's name. This is where for most people a little security through obscurity works, for example:

-(void)initialize_CustomUIView{
// Common init tasks
}
-(id)initWithFrame:(CGRect)frame{
if ((self = [super initWithFrame:frame])){
[self initialize_CustomUIView];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if ((self = [super initWithCoder:aDecoder])){
[self initialize_CustomUIView];
}
return self;
}
-(id)init{
if ((self = [super init])){
[self initialize_CustomUIView];
}
return self;
}

But again, you asked specifically how to disable yourself from overriding the common initialization method, thus the static void initializeInstance() solution.

Swift protocol extension method is called instead of method implemented in subclass

This is just how protocols currently dispatch methods.

A protocol witness table (see this WWDC talk for more info) is used in order to dynamically dispatch to implementations of protocol requirements upon being called on a protocol-typed instance. All it is, is really just a listing of the function implementations to call for each requirement of the protocol for a given conforming type.

Each type that states its conformance to a protocol gets its own protocol witness table. You'll note that I said "states its conformance", and not just "conforms to". BaseClass gets its own protocol witness table for conformance to MyProtocol. However SubClass does not get its own table for conformance to MyProtocol – instead, it simply relies on BaseClass's. If you moved the
: MyProtocol down to the definition of SubClass, it would get to have its own PWT.

So all we have to think about here is what the PWT for BaseClass looks like. Well, it doesn't provide an implementation for either of the protocol requirements methodA() or methodB() – so it relies on the implementations in the protocol extension. What this means is that the PWT for BaseClass conforming to MyProtocol just contains mappings to the extension methods.

So, when the extension methodB() method is called, and makes the call out to methodA(), it dynamically dispatches that call through the PWT (as it's being called on a protocol-typed instance; namely self). So when this happens with a SubClass instance, we're going through BaseClass's PWT. So we end up calling the extension implementation of methodA(), regardless of the fact that SubClass provides an implementation of it.

Now let's consider the PWT of JustClass. It provides an implementation of methodA(), therefore its PWT for conformance to MyProtocol has that implementation as the mapping for methodA(), as well as the extension implementation for methodB(). So when methodA() is dynamically dispatched via its PWT, we end up in its implementation.

As I say in this Q&A, this behaviour of subclasses not getting their own PWTs for protocols that their superclass(es) conform to is indeed somewhat surprising, and has been filed as a bug. The reasoning behind it, as Swift team member Jordan Rose says in the comments of the bug report, is

[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.

Therefore if this was the behaviour, already-compiled subclasses would lack any PWTs from superclass conformances that were added after the fact in another module, which would be problematic.


As others have already said, one solution in this case is to have BaseClass provide its own implementation of methodA(). This method will now be in BaseClass's PWT, rather than the extension method.

Although of course, because we're dealing with classes here, it won't just be BaseClass's implementation of the method that's listed – instead it will be a thunk that then dynamically dispatches through the class' vtable (the mechanism by which classes achieve polymorphism). Therefore for a SubClass instance, we'll wind up calling its override of methodA().



Related Topics



Leave a reply



Submit