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: 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:
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.Book
has an implicit memberwise initializerinit(title:author:description:)
which simply initializes its fields to the given parameters.An extension is made which compartmentalizes all dictionary related tasks into a single unit.
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 returnnil
, without crashing your program.A
static
method is made on theBook
struct,books(fromDictArray:)
, which will create an array of optional books ([Book?]
, a.k.aArray<Optional<Book>>
out of the given dict. It is then the job of the consumer of this method to deal with thenil
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.
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
How to Animate a Model's Rotation in Realitykit
Uitableviewautomaticdimension Not Working for Resizing Cell Height
Iboutlet of Another View Controller Is Nil
Using Combine's Future to Replicate Async Await in Swift
How to Make an Array of the Current Week Dates Swift
How to Generate Large, Ranged Random Numbers in Swift
Swift Performsegue Going to Xcode
Swift: Orient Y-Axis Toward Another Point in 3-D Space
How Does One Trap Arithmetic Overflow Errors in Swift
Check Whether Swift Object Is an Instance of a Given Metatype
Swift Watchos 2 - Cmsensordatalist
What Causes 'Constant Captured by a Closure Before Being Initialized' Error
Swift - Could Not Cast Value of Type '_Nscfstring' to 'Nsdictionary'
Swift: Failing to Copy Files to a Newly Created Folder
How to Get User Home Directory Path (Users/"User Name") Without Knowing the Username in Swift3
How to Check Two Instances Are the Same Class/Type in Swift
Swift Generics: Requiring Addition and Multiplication Abilities of a Type