Подтвердить что ты не робот

Быстрое вызов статических методов: type (of: self) vs явное имя класса

В swift экземпляр func не может вызвать static/class func без префикса вызова метода с именем класса. ИЛИ вы можете использовать type(of: self), например

class Foo {
    static func doIt() { }

    func callIt() {
        Foo.doIt() // This works
        type(of: self).doIt() // Or this

        doIt() // This doesn't compile (unresolved identifier)
    }
}

Мой вопрос в том, какая разница здесь? Это просто вопрос стиля кодирования, или есть какая-то разница, например. статическая или динамическая отправка?

Если это только стиль кодирования, какой предпочтительный стиль?

4b9b3361

Ответ 1

Существуют два основных отличия.

1. Значение self внутри статического метода

Метатип, который вы называете статическим методом, доступен вам в методе как self (он просто передается как неявный параметр). Поэтому, если вы назовете doIt() на type(of: self), self будет динамическим метатипом экземпляра. Если вы назовете его Foo, self будет Foo.self.

class Foo {
    static func doIt() {
        print("hey I'm of type \(self)")
    }

    func callDoItOnDynamicType() {
        type(of: self).doIt() // call on the dynamic metatype of the instance.
    }

    func classDoItOnFoo() {
        Foo.doIt() // call on the metatype Foo.self.
    }
}

class Bar : Foo {}

let f: Foo = Bar()

f.callDoItOnDynamicType() // hey I'm of type Bar
f.classDoItOnFoo()        // hey I'm of type Foo

Это различие может быть действительно важным для методов factory, поскольку оно определяет тип создаваемого экземпляра.

class Foo {
    required init() {}

    static func create() -> Self {
        return self.init()
    }

    func createDynamic() -> Foo {
        return type(of: self).create()
    }

    func createFoo() -> Foo {
        return Foo.create()
    }
}

class Bar : Foo {}

let f: Foo = Bar()

print(f.createDynamic()) // Bar
print(f.createFoo())     // Foo

2. Отправка статического метода

(Мартин уже рассмотрел это, но я думал, что добавлю его ради завершения.)

Для class методов, переопределенных в подклассах, значение метатипа, вызываемого методом, определяет, какую реализацию вызывать.

Если вызывается в метатипе, который известен во время компиляции (например, Foo.doIt()), Swift может статически отправлять вызов. Однако, если вы вызываете метод по метатипу, который неизвестен до времени выполнения (например, type(of: self)), вызов метода будет динамически отправляться в правильную реализацию для значения метатипа.

class Foo {
    class func doIt() {
        print("Foo doIt")
    }

    func callDoItOnDynamicType() {
        type(of: self).doIt() // the call to doIt() will be dynamically dispatched.
    }

    func classDoItOnFoo() {
        Foo.doIt() // will be statically dispatched.
    }
}


class Bar : Foo {
    override class func doIt() {
        print("Bar doIt")
    }
}

let f: Foo = Bar()

f.callDoItOnDynamicType() // Bar doIt
f.classDoItOnFoo()        // Foo doIt

Ответ 2

Для метода class это имеет значение, если метод переопределено в подклассе:

class Foo {
    class func doIt() {
        print("Foo doit")
    }

    func callViaClassname() {
        Foo.doIt()
    }

    func callViaTypeOf() {
        type(of: self).doIt()
    }
}

class Bar: Foo {
    override class func doIt() {
        print("Bar doit")
    }

}

Bar().callViaClassname() // Foo doit
Bar().callViaTypeOf() // Bar doit

Это также описано в "Типы" в справочнике Swift Language:

Вы можете использовать выражение type(of:) с экземпляром типа для доступа к этим экземплярам dynamic, runtime type как value,...

Я не знаю разницы для метода static (который равен final и не может быть переопределен в подклассе). Исправление: см. Хэмиш ответ за разницу как в статических, так и в классах.