Help us understand the problem. What is going on with this article?

Swift5.1のattribute全解説|全27種

概要

Swift5.1で利用できるattributeの一覧です。(Public APIのみ)

本記事は以前作成したSwiftのattributeまとめ[Swift4対応]をSwift5.1向けに更新したものです。

attributeは全てを暗記する必要はありません。これなんだっけ、と思ったときにこの記事でさくっと見られる簡易リファレンスになればと思います。

attributeとは

  • コンパイラに対し、宣言や型の補足情報を伝えるもの
  • 属性や修飾子とも呼ばれる
  • Swift5.1では公式リファレンスに記載されているもので全27種類

attributeの記法

  • attributeの記法は以下のようになり、より詳細な情報を補足するために引数も指定することができる
  • @〜という記法はコンパイラディレクティブと呼ばれ、コンパイラに対する指示を記載する際に利用される
// 引数なしの場合
@属性名

// 引数ありの場合
@属性名引数

attribute一覧

  • autoclosure
  • escaping
  • convention
  • available
  • discardableResult
  • objc
  • nonobjc
  • objcMembers
  • GKInspectable
  • UIApplicationMain
  • NSApplicationMain
  • NSCopying
  • NSManaged
  • testable
  • IBAction
  • IBOutlet
  • IBDesignable
  • IBInspectable
  • dynamicCallable
  • dynamicMemberLookup
  • frozen
  • inlinable
  • usableFromInline
  • propertyWrapper
  • requires_stored_property_inits
  • warn_unqualified_access
  • unknown

※ドキュメントに記載のあるPublic APIのみです。

@autoclosure

  • 以下のように、呼び出し部分で引数として渡した値を、メソッド(関数)内ではクロージャーとして扱える
  • クロージャーとして渡しているため、引数が実際に評価されるのは、クロージャーの実行時(遅延評価が可能)
// 定義部分
func someMethod(closure: @autoclosure () -> Int) {
    print(closure())
}

// 呼び出し部分
someMethod(closure: 10)

利用シーンは少ないと想定するが、&&演算子や||演算子の実装などで利用

&&演算子、||演算子の実装
public static func &&(lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool
public static func ||(lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows -> Bool

@escaping

  • クロージャをスコープ外でも保持する必要があることを示す
  • 以下の例では、completionクロージャーの実行を非同期で実行しているため、スコープ外ではクロージャーが保持されず、コンパイルエラー
// NG: コンパイルエラー
func someAsyncMethod(completion: () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

// OK
func someAsyncMethod(completion: @escaping () -> Void) {
    DispatchQueue.main.async {
       completion()
    }
}

@convention

  • 関数のポインタに対し、その呼び出し方式を指定
// Swiftの関数ポインタ(デフォルト)
var swiftFunction: @convention(swift) (Bool) -> Bool

// Objective-Cと互換性をもつブロックポインタ
var block: @convention(block) (Bool) -> Bool

// C言語の関数ポインタ
var cFunction: @convention(c) (Bool) -> Bool

@available

  • 各OSバージョンなどの環境に対し、APIの有効性を表す
  • Swiftのバージョン(swift)
  • プラットフォームの種類(iOS,iOSApplicationExtension, macOS, macOSApplicationExtension, watchOS, watchOSApplicationExtension ,tvOS, tvOSApplicationExtension)
// 全環境(*)で利用不可(unavailable)、SomeProtocolにリネームした(renamed:)
@available(*, unavailable, renamed: "SomeProtocol")
protocol MyProtocol { ... }

// macOS 10.12で廃止(obsoleted)とメッセージを表示(message)
@available(macOS 10.12, obsoleted: 10.12, message: "macOS 10.12で廃止されました")
func someMethod() { ... }

// iOS2で導入(introduced)、iOS9で非推奨(deprecated)
@available(iOS, introduced: 2.0, deprecated: 9.0)
var some: String

例えば、iOS9で非推奨となった、UIAlertViewは以下のようにattribute指定することで、UIAlertViewを利用した際にコンパイラ警告を出し、UIAlertControllerの利用を推奨する。

スクリーンショット 2017-06-30 4.50.06.png

UIAlertView.h
@available(iOS, introduced: 2.0, deprecated: 9.0, 
message: "UIAlertView is deprecated. Use UIAlertController with a 
preferredStyle of UIAlertControllerStyleAlert instead")
open class UIAlertView : UIView { ... }

@discardableResult

  • 返り値を持つ関数やメソッドの返り値を利用しなかった場合のコンパイラ警告を無視する
  • 返り値を必ずしも利用しなくて良いメソッドを定義するのに適する
// メソッドの定義部分
func someMethod1() -> Bool { ... }

@discardableResult
func someMethod2() -> Bool { ... }

// 呼び出し部分
someMethod1() // NG:メソッドの返り値を利用していないためコンパイラ警告がでる
someMethod2() // OK

@discardableResultを指定しない場合は「Result of call to 'someMethod()' is unused」とwarningが表示される。
スクリーンショット 2017-06-30 4.57.07.png

@objc

  • Objective-Cから使用できることを明示的に宣言
  • extensionに指定すると全てのメンバに一括で指定できる
  • @objc(引数)でObjective-Cで使いたい名前を指定できる
  • Swift4以前ではNSObjectを継承したクラスやdynamicでは暗黙的に@objcが付け加えられていましたが、Swift4では付与されなくなりました(Proposal: SE-0160
// someMethodとしてObjective-Cから利用可能
@objc(someMethod)
func someMethodForObjc() { ... }

@nonobjc

  • Objective-Cから使用できないことを明示的に宣言
  • extensionに指定すると全てのメンバに一括で指定できる
@nonobjc
func SomeMethodForSwift() { ... }

@objcMembers

  • クラス全体に対し一括でObjective-Cから使用できることを明示的に宣言
  • @objcMembersを付与したクラスのサブクラスやエクステンションにも影響
  • Swift4で導入(Proposal: SE-0160
@objcMembers
class MyClass : NSObject {
    // @objcになる
    func foo() { }             

    // @objcにならない(タプルを返しているため)
    func bar() -> (Int, Int) { return (1, 1) }   
}

extension MyClass {
    // @objcになる
    func baz() { }   
}

class MySubClass : MyClass {
    // @objcになる
    func wibble() { } 
}

extension MySubClass {
    // @objcになる
    func wobble() { }   
}

@GKInspectable

  • カスタムのGameplayKitコンポーネントプロパティをSpriteKitエディタのUIに公開する
  • 暗黙的に@objcが付け加えられる
class MyComponent: GKComponent {
    @GKInspectable var speed: Float = 1.0
    @GKInspectable var friction: Float = 2.0
}

@UIApplicationMain

  • アプリケーションデリゲートであることを示す(iOSアプリ用)
  • この属性がない場合はmain.swiftを用意し、UIApplicationMain(::_:)関数を利用してデリゲート設定を行う
例(AppDelegate.swift)
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions
        launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        return true
    }
    func applicationWillResignActive(_ application: UIApplication) { }
    func applicationDidEnterBackground(_ application: UIApplication) { }
    func applicationWillEnterForeground(_ application: UIApplication) { }
    func applicationDidBecomeActive(_ application: UIApplication) { }
    func applicationWillTerminate(_ application: UIApplication) { }
}

@NSApplicationMain

  • アプリケーションデリゲートであることを示す(macアプリ用)
  • この属性がない場合はmain.swiftを用意し、UIApplicationMain(::_:)関数を利用してデリゲート設定を行う
例(AppDelegate.swift)
import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    func applicationDidFinishLaunching(_ aNotification: Notification) { }
    func applicationWillTerminate(_ aNotification: Notification) { }
}

@NSCopying

  • ストアドプロパティのセッターでコピーした値をセットする
  • Objective-Cのcopy属性と同様
  • Swift4でイニシャライザでの挙動が改善されました(Proposal: SE-0153
@NSCopying var foo: Foo

@NSManaged

  • クラスのインスタンスメソッドやストアドプロパティに対し、CoreDataで実行時に動的に実装が生成されることを宣言する
@NSManaged var name: String

@testable

  • モジュールのimport宣言に対し、internal以上のアクセスレベルで公開されているメソッドやプロパティに対しテストクラスがアクセス可能なことを宣言する
@testable import SomeModule

@IBAction

  • メソッドがInterfaceBuilder(StoryBoard)に配置したパーツのアクションに紐付けられることを示す
  • 暗黙的に@objcが付け加えられる
@IBAction func buttonTapped() { ... }

@IBOutlet

  • プロパティがInterfaceBuilder(StoryBoard)に配置したパーツに紐づけられることを示す
  • 暗黙的に@objcが付け加えられる
@IBOutlet weak var detailDescriptionLabel: UILabel!

@IBDesignable

  • UIViewまたはNSViewを継承したカスタムクラスに指定するとデザインやサブビューがInterfaceBuilder(StoryBoard)上でライブレンダリングされる
  • 暗黙的に@objcが付け加えられる
@IBDesignable class CustomView: UIView { ... }

@IBInspectable

  • プロパティに指定すると、InterfaceBuilder(StoryBoard)のAttribute Inspectorで設定でき、ライブレンダリングでデザインを確認できる
  • 対応する型(Int, CGFloat, Double, String, Bool, CGPoint, CGSize, CGRect, UIColor, UIImage)
  • 暗黙的に@objcが付け加えられる
@IBInspectable 
public var cornerRadius: CGFloat = 2.0 { 
    didSet { 
        self.layer.cornerRadius = self.cornerRadius 
    } 
}

@IBSegueAction

  • StoryboardでSegueを設定し、画面遷移する際の処理を指定できる
  • 引数のNSCoderインスタンスを利用し、遷移先のViewControllerの任意のイニシャライザを指定することが可能
@IBSegueAction
private func openDetail(coder: NSCoder, sender: Any?, segueIdentifier: String?)
    -> DetailViewController? {
    return DetailViewController(coder: coder, item: item)
}

@dynamicCallable

  • 型に@dynamicCallableを付与し、dynamicallyCall(withArguments:)メソッドもしくはdynamicallyCall(withKeywordArgument:)メソッドを実装することで、その型を関数のように直接呼び出しができるようになる
@dynamicCallable struct Sum {
    // 引数ラベルなし
    func dynamicallyCall(withArguments args: [Int]) -> Int {
        return args.reduce(0, +)
    }

    // 引数ラベルあり
    func dynamicallyCall(withKeywordArguments args: KeyValuePairs<String, Int>) -> Int {
        return args.map { $0.value }.reduce(0, +)
    }
}

let sum = Sum()
print(sum(1, 2, 3)) // 6
print(sum(first: 1, second: 2, third: 3)) // 6

@dynamicMemberLookup

  • 型に@dynamicMemberLookupを付与し、subscript(dynamicMember:)を実装することで、コンパイル時に存在しないプロパティに.でアクセスが可能となる
@dynamicMemberLookup struct Dog {
    subscript(dynamicMember key: String) -> String {
        return key
    }

    subscript(dynamicMember key: Int) -> Int {
        return key
    }
}

let dog = Dog()
print(dog.name) // "name"
print(dog.1) // 1

@frozen

  • library evolution mode(-enable-library-evolutionオプション)でコンパイル時のみ有効
  • structもしくはenumに付与し、将来のバージョンでの型の変更を制限する
  • structではストアドプロパティ、enumではcaseの追加、削除、並び替えを制限する
  • library evolution modeでない場合には全てのstruct、enumが暗黙的に@frozenとなる
@frozen enum IceCream {
    case vanilla
    case chocolate
    case greenTea
}

@inlinable

  • 関数、メソッド、コンピューテッドプロパティ、subscript、コンビニエンスイニシャライザ、デイニシャライザに付与することで、実装をmodule interfaceの一部として公開する(インライン展開)
  • publicもしくはinternalで宣言されたものにのみ付与可能(実際にはinternalで定義された@inlinable@usableFromInlineを意味する)
@inlinable func hoge() {
    print("hoge")
}

@usableFromInline

  • @inlinableの実装でアクセスする宣言に対して必要なattribute
  • アクセス制御修飾子をサポートするすべての宣言に適用可能
  • internalで宣言されたものにのみ付与可能
@inlinable func hoge() {
    print("hoge")
    fuga()
}

@usableFromInline func fuga() {
    print("fuga")
}

@propertyWrapper

  • プロパティの値にアクセスする方法を独自のattributeとして定義できる
  • attributeを定義するにはこのattributeをclass,enum,structのいずれかに付与
  • アクセス方法はwrappedValueプロパティのget、setを定義することで記述
  • projectedValueプロパティを定義することで、$プロパティ名でアクセス可能な値を定義できる
  • イニシャライザでattributeに初期値を与えることも可能
@propertyWrapper struct SmallNumber {
    private var max: Int
    private var number: Int
    var projectedValue = false

    var wrappedValue: Int {
        get { return number }
        set {
            number = min(newValue, max)
            projectedValue = newValue > max
        }
    }

    init(wrappedValue: Int, max: Int) {
        self.number = min(wrappedValue, max)
        self.max = max
    }
}

struct SmallPoint {
    @SmallNumber(wrappedValue: 5, max: 10) var x: Int
    @SmallNumber(wrappedValue: 5, max: 10) var y: Int
}

var smallPoint = SmallPoint()
print(smallPoint.x) // 5
print(smallPoint.$x) // false

smallPoint.x = 200
print(smallPoint.$x) // true
print(smallPoint.x) // 10

@requires_stored_property_inits

  • クラス定義に付与することで、すべてのストアドプロパティのデフォルト値を要求する
  • NSManagedObjectを継承するクラスでは暗黙的に付与される
@requires_stored_property_inits
class Hoge {
    // error: Stored property 'number' requires an initial value
    let number: Int

    init(number: Int) {
        self.number = number
    }
}

@warn_unqualified_access

  • トップレベルの関数、インスタンスメソッド、classメソッド、staticメソッドに付与することで、モジュール名や型名、インスタンス変数やインスタンス定数など、メソッドを特定するものがない場合にwarningを発生させる
  • 同一スコープ内で同名の関数にアクセス可能な場合に曖昧さをなくすのに役立つ
  • Sequenceのmin()で利用
@warn_unqualified_access func min() -> Self.Element? { 
   /// 
}

@unknown

  • 将来的に新しいcaseが追加された場合に、そのケースをhandlingできるようにする
  • コンパイル時にはenumが網羅されていない旨のwarningが発生する
enum Signal {
    case red
    case yellow
    case green
    case purple // 新しいcase
}

let signal = Signal.yellow

switch signal { // warning: Switch must be exhaustive
case .red:
    print("red")
case .yellow:
    print("yellow")
case .green:
    print("green")
@unknown default:
    print("未知のケース")
}

参考

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away