Swift Equivalent to _Attribute((Objc_Requires_Super))

Swift equivalent to __attribute((objc_requires_super))?

No, there is no Swift equivalent to __attribute((objc_requires_super)).

The equivalent feature, Swift Attributes, contains no such attribute.

The section of the Swift inheritance documentation where such a feature would be mentioned says only:

When you provide a method, property, or subscript override for a subclass, it is sometimes useful to use the existing superclass implementation as part of your override.


Note that you can prevent overriding functions using final, so you could effectively accomplish what you want by providing empty overridable methods that are called by non-overridable methods:

class AbstractStarship {
var tractorBeamOn = false

final func enableTractorBeam() {
tractorBeamOn = true

println("tractor beam successfully enabled")

tractorBeamDidEnable()
}

func tractorBeamDidEnable() {
// Empty default implementation
}
}

class FancyStarship : AbstractStarship {
var enableDiscoBall = false

override func tractorBeamDidEnable() {
super.tractorBeamDidEnable() // this line is irrelevant

enableDiscoBall = true
}
}

Subclasses would then override the overridable methods, and it wouldn't matter whether they called super or not since the superclass's implementation is empty.

As Bryan Chen notes in the comments, this breaks down if the subclass is subclassed.

I make no claims to whether this approach is stylistically good, but it is certainly possible.

What is the Swift equivalent of NS_UNAVAILABLE?

You can use @available by marking the function as @available(*, unavailable).

You can read up on it here under Declaration Attributes.

Swift Sample Image 34

Swift: Alternatives to super class methods to generate objects

Here is how I would write this code in idiomatic Swift:

struct Book {
let title: String
let author: String
let description: String

/* an implicit member wise initializer is generated,
which would otherwise look something like this:
init(title: String, author: String, description: String) {
self.title = title
self.author = author
self.description = description
} */
}

// Initialization from Dictionaries
extension Book {
init?(fromDict dict: [String: Any]) {
guard
let title = dict["bookTitle"] as? String,
let author = dict["author"] as? String,
let description = dict["description"] as? String
else { return nil }

self.init(
title: title,
author: author,
description: description
)
}

static func books(fromDictArray array: [[String: Any]]) -> [Book?] {
return array.map(Book.init)
}
}

Here are some notable points:

  1. Book is a struct. Such a broad description of a book doesn't need to support the notion of identity. I.e., your book named "Harry Potter", by "J.K. Rowling" with the description "Some description" can be considered to be the same as my book with the same values. There's no apparent need (yet) to distinguish the identity of your book vs the identity of mine.

  2. Book has an implicit memberwise initializer init(title:author:description:) which simply initializes its fields to the given parameters.

  3. An extension is made which compartmentalizes all dictionary related tasks into a single unit.

  4. A failable initializer, init?(fromDictArray:) is made, which returns a new book based off the given dict (presumably created from your JSON). This initializer is fault tolerant. If the dict provided is invalid, then the initializer will simply return nil, without crashing your program.

  5. A static method is made on the Book struct, books(fromDictArray:), which will create an array of optional books ([Book?], a.k.a Array<Optional<Book>> out of the given dict. It is then the job of the consumer of this method to deal with the nil values, those resulting from invalid dicts, as they please.

    • They could ignore the nil books:

      let books = Book.books(fromDictArray: myDictArray).flatMap{$0}
    • They could crash if a nil book is found:

      let books = Book.books(fromDictArray: myDictArray) as! [Book]
    • Or they can handle the nil cases in some unique way:

      let books = Book.books(fromDictArray: myDictArray).map{ book in
      if book == nil {
      print("A nil book was found")
      }
      }

What is the Swift equivalent of respondsToSelector?

As mentioned, in Swift most of the time you can achieve what you need with the ? optional unwrapper operator. This allows you to call a method on an object if and only if the object exists (not nil) and the method is implemented.

In the case where you still need respondsToSelector:, it is still there as part of the NSObject protocol.

If you are calling respondsToSelector: on an Obj-C type in Swift, then it works the same as you would expect. If you are using it on your own Swift class, you will need to ensure your class derives from NSObject.

Here's an example of a Swift class that you can check if it responds to a selector:

class Worker : NSObject
{
func work() { }
func eat(food: AnyObject) { }
func sleep(hours: Int, minutes: Int) { }
}

let worker = Worker()

let canWork = worker.respondsToSelector(Selector("work")) // true
let canEat = worker.respondsToSelector(Selector("eat:")) // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:")) // true
let canQuit = worker.respondsToSelector(Selector("quit")) // false

It is important that you do not leave out the parameter names. In this example, Selector("sleep::") is not the same as Selector("sleep:minutes:").

Swift native base class or NSObject

Swift classes that are subclasses of NSObject:

  • are Objective-C classes themselves
  • use objc_msgSend() for calls to (most of) their methods
  • provide Objective-C runtime metadata for (most of) their method implementations

Swift classes that are not subclasses of NSObject:

  • are Objective-C classes, but implement only a handful of methods for NSObject compatibility
  • do not use objc_msgSend() for calls to their methods (by default)
  • do not provide Objective-C runtime metadata for their method implementations (by default)

Subclassing NSObject in Swift gets you Objective-C runtime flexibility but also Objective-C performance. Avoiding NSObject can improve performance if you don't need Objective-C's flexibility.

Edit:

With Xcode 6 beta 6, the dynamic attribute appears. This allows us to instruct Swift that a method should use dynamic dispatch, and will therefore support interception.

public dynamic func foobar() -> AnyObject {
}

What is the Swift equivalent of NS_UNAVAILABLE?

You can use @available by marking the function as @available(*, unavailable).

You can read up on it here under Declaration Attributes.

Swift Sample Image 35

Give warning when [super method] is not called

Recent versions of llvm have added an attribute that indicates that subclasses must call super:

@interface Barn:NSObject
- (void)openDoor NS_REQUIRES_SUPER;
@end

@implementation Barn
- (void) openDoor
{
;
}
@end

@interface HorseBarn:Barn
@end
@implementation HorseBarn
- (void) openDoor
{
;
}
@end

Compiling the above produces the warning:

Method possibly missing a [super openDoor] call

Swift property access in ObjC++

"G" in your Objective C code is a class, so what you appear to be trying to do is get a property from a class, not an instance of that class.

You can access a property from an instance (or object) of a class, so G needs to be a parameter in your initWithFrame or it needs to be its own property in your Obj-C object (e.g. self.G.hST).

Another thing you can do is declare your hST property as a class function instead (e.g. look at this related question). That is:

@objc class G: NSObject {

class func hST() -> CGFloat {
return 200
}
}

Class does not implement its superclass's required members

From an Apple employee on the Developer Forums:

"A way to declare to the compiler and the built program that you really
don't want to be NSCoding-compatible is to do something like this:"

required init(coder: NSCoder) {
fatalError("NSCoding not supported")
}

If you know you don't want to be NSCoding compliant, this is an option. I've taken this approach with a lot of my SpriteKit code, as I know I won't be loading it from a storyboard.


Another option you can take which works rather well is to implement the method as a convenience init, like so:

convenience required init(coder: NSCoder) {
self.init(stringParam: "", intParam: 5)
}

Note the call to an initializer in self. This allows you to only have to use dummy values for the parameters, as opposed to all non-optional properties, while avoiding throwing a fatal error.


The third option of course is to implement the method while calling super, and initialize all of your non-optional properties. You should take this approach if the object is a view being loaded from a storyboard:

required init(coder aDecoder: NSCoder!) {
foo = "some string"
bar = 9001

super.init(coder: aDecoder)
}


Related Topics



Leave a reply



Submit