Сравнение функций в быстрых [дубликатов]

Я нахожу это правило ошибочным и громоздким (но тогда какая часть множественного наследования отсутствует?).

Но логически наложенный порядок построения должен отличаться от случая нормального (не- -виртуальное) наследование. Рассмотрим пример Ajay, минус virtual:

class A;
class B1 : A;
class B2 : A;
class C: B1,B2

В этом случае для каждого C два As построены, один из них является частью конструкции B1, а другой - частью конструкции B2. Код класса B отвечает за это и может это сделать. Порядок событий:

Start C ctor
   Start B1 ctor
      A ctor (in B's ctor code)
   End B1 ctor
   Start B2 ctor
      A ctor (in B's ctor code)
   End B2 ctor
End C ctor

Теперь рассмотрим виртуальное наследование в

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 

. Один порядок события -

Start C ctor
   Start B1 ctor
      // NO A ctor
   End B1 ctor
   Start B2 ctor
      // NO A ctor
   End B2 ctor

   A ctor // not B's code!
End C ctor

Возможно, что A строится до B1 и B2, но это не имеет значения. Важно то, что конструкция А не происходит при построении других базовых классов. Этот код просто не выполняется и не может быть выполнен, потому что фактически унаследованный под-объект базового класса типа A является частью и под управлением самого производного класса , который недоступен из B1 и Код B2; действительно, C даже не полностью сконструирован в момент времени B1 или B2, и он может потенциально попытаться создать A в C.

73
задан Jessy 21 January 2016 в 17:19
поделиться

7 ответов

Крис Лэттнер написал на форумах разработчиков:

Это функция, которую мы намеренно не хотим поддерживать. Существует множество вещей, которые вызовут равенство функций указателя (в смысле системы быстрого типа, которое включает в себя несколько видов замыканий) для отказа или изменения в зависимости от оптимизации. Если для функций были определены «===», компилятору не разрешалось бы объединять идентичные тела методов, делиться ими и выполнять определенные оптимизации захвата при закрытии. Кроме того, равенство такого рода было бы чрезвычайно удивительным в некоторых контекстах дженериков, где вы можете получить кривые ревальментации, которые корректируют действительную подпись функции до той, которую ожидает тип функции.

https://devforums.apple.com/message/1035180#1035180

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

54
ответ дан drewag 27 August 2018 в 22:26
поделиться

Вот одно возможное решение (концептуально такое же, как ответ «tuncay»). Дело в том, чтобы определить класс, который обертывает некоторые функции (например, Command):

Swift:

typealias Callback = (Any...)->Void
class Command {
    init(_ fn: @escaping Callback) {
        self.fn_ = fn
    }

    var exec : (_ args: Any...)->Void {
        get {
            return fn_
        }
    }
    var fn_ :Callback
}

let cmd1 = Command { _ in print("hello")}
let cmd2 = cmd1
let cmd3 = Command { (_ args: Any...) in
    print(args.count)
}

cmd1.exec()
cmd2.exec()
cmd3.exec(1, 2, "str")

cmd1 === cmd2 // true
cmd1 === cmd3 // false

Java:

interface Command {
    void exec(Object... args);
}
Command cmd1 = new Command() {
    public void exec(Object... args) [
       // do something
    }
}
Command cmd2 = cmd1;
Command cmd3 = new Command() {
   public void exec(Object... args) {
      // do something else
   }
}

cmd1 == cmd2 // true
cmd1 == cmd3 // false
2
ответ дан baso 27 August 2018 в 22:26
поделиться

Я тоже искал ответ. И я нашел его наконец.

Что вам нужно - это фактический указатель на функцию и его контекст, скрытый в объекте функции.

func peekFunc<A,R>(f:A->R)->(fp:Int, ctx:Int) {
    typealias IntInt = (Int, Int)
    let (hi, lo) = unsafeBitCast(f, IntInt.self)
    let offset = sizeof(Int) == 8 ? 16 : 12
    let ptr  = UnsafePointer<Int>(lo+offset)
    return (ptr.memory, ptr.successor().memory)
}
@infix func === <A,R>(lhs:A->R,rhs:A->R)->Bool {
    let (tl, tr) = (peekFunc(lhs), peekFunc(rhs))
    return tl.0 == tr.0 && tl.1 == tr.1
}

И вот демо:

// simple functions
func genericId<T>(t:T)->T { return t }
func incr(i:Int)->Int { return i + 1 }
var f:Int->Int = genericId
var g = f;      println("(f === g) == \(f === g)")
f = genericId;  println("(f === g) == \(f === g)")
f = g;          println("(f === g) == \(f === g)")
// closures
func mkcounter()->()->Int {
    var count = 0;
    return { count++ }
}
var c0 = mkcounter()
var c1 = mkcounter()
var c2 = c0
println("peekFunc(c0) == \(peekFunc(c0))")
println("peekFunc(c1) == \(peekFunc(c1))")
println("peekFunc(c2) == \(peekFunc(c2))")
println("(c0() == c1()) == \(c0() == c1())") // true : both are called once
println("(c0() == c2()) == \(c0() == c2())") // false: because c0() means c2()
println("(c0 === c1) == \(c0 === c1)")
println("(c0 === c2) == \(c0 === c2)")

См. URL-адреса ниже, чтобы узнать, почему и как это работает:

Как вы видите, он способен проверять только личность ( второй тест дает false). Но это должно быть достаточно хорошим.

7
ответ дан dankogai 27 August 2018 в 22:26
поделиться

Это отличный вопрос, и хотя Крис Лэттнер намеренно не хочет поддерживать эту функцию, я, как и многие разработчики, также не могу отпустить мои чувства, исходящие с других языков, где это тривиальная задача. Существует множество примеров unsafeBitCast, большинство из которых не показывают полную картину, здесь более подробный :

typealias SwfBlock = () -> ()
typealias ObjBlock = @convention(block) () -> ()

func testSwfBlock(a: SwfBlock, _ b: SwfBlock) -> String {
    let objA = unsafeBitCast(a as ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testObjBlock(a: ObjBlock, _ b: ObjBlock) -> String {
    let objA = unsafeBitCast(a, AnyObject.self)
    let objB = unsafeBitCast(b, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

func testAnyBlock(a: Any?, _ b: Any?) -> String {
    if !(a is ObjBlock) || !(b is ObjBlock) {
        return "a nor b are ObjBlock, they are not equal"
    }
    let objA = unsafeBitCast(a as! ObjBlock, AnyObject.self)
    let objB = unsafeBitCast(b as! ObjBlock, AnyObject.self)
    return "a is ObjBlock: \(a is ObjBlock), b is ObjBlock: \(b is ObjBlock), objA === objB: \(objA === objB)"
}

class Foo
{
    lazy var swfBlock: ObjBlock = self.swf
    func swf() { print("swf") }
    @objc func obj() { print("obj") }
}

let swfBlock: SwfBlock = { print("swf") }
let objBlock: ObjBlock = { print("obj") }
let foo: Foo = Foo()

print(testSwfBlock(swfBlock, swfBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testSwfBlock(objBlock, objBlock)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false

print(testObjBlock(swfBlock, swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testObjBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testAnyBlock(swfBlock, swfBlock)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(objBlock, objBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

print(testObjBlock(foo.swf, foo.swf)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: false
print(testSwfBlock(foo.obj, foo.obj)) // a is ObjBlock: false, b is ObjBlock: false, objA === objB: false
print(testAnyBlock(foo.swf, foo.swf)) // a nor b are ObjBlock, they are not equal
print(testAnyBlock(foo.swfBlock, foo.swfBlock)) // a is ObjBlock: true, b is ObjBlock: true, objA === objB: true

. Интересная часть заключается в том, как быстро бросает SwfBlock в ObjBlock, но на самом деле два литых блока SwfBlock всегда будут разных значений, а ObjBlocks - нет. Когда мы бросаем ObjBlock в SwfBlock, то же самое происходит с ними, они становятся двумя разными значениями. Поэтому, чтобы сохранить ссылку, этого рода литье следует избегать.

Я все еще понимаю весь этот предмет, но одна вещь, о которой я мечтал, - это способность использовать @convention(block) для класса / поэтому я подал запрос функции , который нуждается в голосовании или объяснении, почему это плохая идея. Я также понимаю, что этот подход может быть плохим все вместе, если это так, может кто-нибудь объяснить, почему?

4
ответ дан Ian Bytchek 27 August 2018 в 22:26
поделиться

Хорошо, что прошло 2 дня, и никто не смог найти решение, поэтому я поменяю свой комментарий на ответ:

Насколько я могу судить, вы не можете проверить равенство или личность функций (например, ваш пример) и метаклассов (например, MyClass.self):

Но - и это всего лишь идея - я не могу не заметить, что предложение where в generics , по-видимому, способны проверять равенство типов. Может быть, вы можете использовать это, по крайней мере, для проверки личности?

2
ответ дан Jiaaro 27 August 2018 в 22:26
поделиться

Самый простой способ - обозначить тип блока как @objc_block, и теперь вы можете применить его к AnyObject, сравнимому с ===. Пример:

    typealias Ftype = @objc_block (s:String) -> ()

    let f : Ftype = {
        ss in
        println(ss)
    }
    let ff : Ftype = {
        sss in
        println(sss)
    }
    let obj1 = unsafeBitCast(f, AnyObject.self)
    let obj2 = unsafeBitCast(ff, AnyObject.self)
    let obj3 = unsafeBitCast(f, AnyObject.self)

    println(obj1 === obj2) // false
    println(obj1 === obj3) // true
7
ответ дан matt 27 August 2018 в 22:26
поделиться

Я много искал. Кажется, что нет никакого способа сравнения указателей функций. Лучшим решением, которое я получил, является инкапсуляция функции или закрытия в хешируемый объект. Например:

var handler:Handler = Handler(callback: { (message:String) in
            //handler body
}))
5
ответ дан tuncay 27 August 2018 в 22:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: