Why in Swift We Cannot Adopt a Protocol Without Inheritance a Class from Nsobject

Why in swift we cannot adopt a protocol without inheritance a class from NSObject?

NSURLConnectionDataDelegate itself inherits from NSURLConnectionDelegate which inherits from NSObjectProtocol.

That means that apart from implementing all the methods from NSURLConnectionDataDelegate, and NSURLConnectionDelegate, you also have to implement all the methods from NSObjectProtocol (e.g. equality, hash).

You didn't implement them, that's your mistake. If you inherit from NSObject, all that NSObjectProtocol methods are already implemented for you.

Cannot adopt a swift class to an objective C protocol of type X NSObject

Actually, there is no need to implement NSObject's methods.
To be able to subclass/inherit from Objective-C classes/protocols you just need to inherit from NSObject. This is to make sure that your Swift class can be used in Objective-C environment as well. Also, Swift structs can't adopt Objective-C protocols, as there is no such type in Obj-C

This should work

class MyClass: NSObject, OtherProtocol {
}

How can I conform to a protocol if a class has no superclass?

If a class has a superclass, then the superclass must be listed first among the base items. When class has no superclass, protocols are listed starting from the first item.

In other words, if there is no zzzSuperClasss, then you could simply write

class xxxController : yyyDelegate

Swift realizes that yyyDelegate is a protocol, and does what you expect.

Documentation provides an example of such class:

protocol FullyNamed {
var fullName: String { get }
}
class Starship: FullyNamed {
...
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}

AnyClass is NSObjectProtocol... sometimes?

You are correct that WKObject implements the NSObject protocol as it implements the WKObject protocol and this protocol inherits from the NSObject protocol. But that plays no role here.

Certain methods like +isSubclassOfClass: or +instancesRespondToSelector: are not declared in the NSObject protocol, these are just normal class methods of the NSObject class and thus inherited by all sub-classes of NSObject but only by sub-classes of NSObject. Other root classes must implement these themselves if they want to be NSObject compatible, the NSObject protocol won't force them to do so.

Now check out this code from a unit test class:

SEL issubclasssel = @selector(isSubclassOfClass:);
Protocol * nsobjp = @protocol(NSObject);
Class c1 = NSClassFromString(@"NSObject");
XCTAssert(c1);
XCTAssert([c1 conformsToProtocol:nsobjp]);
XCTAssert([c1 instancesRespondToSelector:issubclasssel]);
XCTAssert([c1 isSubclassOfClass:[NSObject class]]);

Class c2 = NSClassFromString(@"WKNSURLRequest");
XCTAssert(c2);
XCTAssert([c2 conformsToProtocol:nsobjp]); // Line 1
XCTAssert([c2 instancesRespondToSelector:issubclasssel]); // Line 2
XCTAssert([c2 isSubclassOfClass:[NSObject class]]); // Line 3

This code crashes at Line 2:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)

And if I comment out Line 2, the code still crashes at Line 3 with exactly the same error. Note that this is not Swift code, nor in any way Swift related, this is pure Objective-C code. It's just wrong Objective-C code, as you will see below.

So WKObject does implement +conformsToProtocol: (Line 1 does not crash), it has to, as this is a requirement of the NSObject protocol, but it doesn't implement +instancesRespondToSelector: or +isSubclassOfClass:, and it doesn't have to, so this is perfectly okay. It is a root class, it doesn't inherit from NSObject and there is no protocol that would require it to implement any of these. It was my mistake above to call these methods; calling not-existing methods on objects is "undefined behavior" and this allows the runtime pretty much anything: ignoring the call, just logging an error, throwing an exception, or just crashing right away; and as objc_msgSend() is a highly optimized function (it has no security checks, that would too expensive for every call), it just crashes.

But apparently Swift sometimes doesn't seem to care. When dealing with Obj-C objects, Swift seems to assume that it can always call one of theses methods that NSObject and any sub-class of it implement, even though no protocol would promise that. And that's why certain Swift code constructs cause a crash with Objective-C root objects that don't inherit from NSObject. And as this assumption is simply wrong. Swift must never call any methods on root objects where it doesn't know for sure that these methods are also implemented. Thus I called this a bug in the Swift-Objc-Bridge at the other question.

Update 1:

Giuseppe Lanza asked:

Why then when I have a pure swift class, I get the class from string
and then I test is NSObjectProtocol I get true?

Personally I think this is also a bug in the Swift Runtime. A pure Swift class does not conform to the NSObjectProtocol. Actually it cannot even conform to it, see answer below.

Giuseppe Lanza asked:

Please note that if I create a protocol that inherits from
NSObjectProtocol and then I try to make PureClass conformance to that
protocol the compiler will complain that PureClass is not
NSObjectProtocol compliant

That's because PureClass would have to implement all required NSObjectProtocol methods to conform to that protocol; see this answer https://stackoverflow.com/a/24650406/15809

However, it cannot even satisfy that requirement, as one requirement of NSObjectProtocol is to implement this method

func `self`() -> Self

and that's simply not possible for a pure Swift class, as when you try to do that, the compiler will complain:

error: method cannot be an implementation of an @objc requirement
because its result type cannot be represented in Objective-C

which is correct, a pure Swift class cannot be represented in Obj-C, so it cannot return the required type.

The Swift documentation also says:

Note that @objc protocols can be adopted only by classes that
inherit from Objective-C classes or other @objc classes.

And currently @objc forces you to inherit from NSObject, which a pure Swift class does not.

Why it is not required for ViewController class to conform to the NSObject Protocol while necessary for other classes

Because, as you can see under "inherits from" in the docs, UIViewController inherits from UIResponder, which in turn inherits from NSObject.

To clarify on protocols vs classes: NSObjectProtocol and UITextFieldDelegate are both protocols. UITextFieldDelegate inherits from NSObjectProtocol - for protocols, this means that in order to conform to UITextFieldDelegate, you also need to conform to NSObjectProtocol. Since UIViewController already conforms to NSObjectProtocol because of the above paragraph, all it needs to do is conform to UITextFieldDelegate. Your ColorizerTextFieldDelegate class, on the other hand, does not get that for free, so that's why you need to conform to NSObjectProtocol.

Class does not conform NSObjectProtocol

See Why in swift we cannot adopt a protocol without inheritance a class from NSObject?

In short, WCSessionDelegate itself inherits from NSObjectProtocol therefore you need to implement methods in that protocol, too. The easiest way to implement those methods is to subclass NSObject:

class BatteryLevel: NSObject, WCSessionDelegate

Note that you are dealing with Obj-C APIs here.

Swift delegate does not inherit NSObject

You are confusing class NSObject (NSObject class) and protocol NSObject (NSObject protocol, in Swift called NSObjectProtocol).

UINavigationControllerDelegate is a protocol and cannot inherit from class NSObject, it inherits from NSObjectProtocol (switch your documentation to Swift, you will see the difference).

How to make a class conform to a protocol in Swift?

Type 'CellDatasDataSource' does not conform to protocol 'NSObjectProtocol'

You have to make your class inherit from NSObject to conform to the NSObjectProtocol. Vanilla Swift classes do not. But many parts of UIKit expect NSObjects.

class CustomDataSource : NSObject, UITableViewDataSource {

}

But this:

Type 'CellDatasDataSource' does not conform to protocol 'UITableViewDataSource'

Is expected. You will get the error until your class implements all required methods of the protocol.

So get coding :)

Does swift inheritance from generic class prevent protocol conformance via extension?

It's the combination of the @objc and the generic. Objective-C knows nothing of Swift generics, so your notion that B should adopt an @objc protocol causes the compiler to throw a wobbly.

You've already detected this from one direction; and you can equally see it from the other. You can make your code compile by deleting the @objc attributes. Or you can make it compile by removing the generic. You can't have both at once.



Related Topics



Leave a reply



Submit