Я нахожу это правило ошибочным и громоздким (но тогда какая часть множественного наследования отсутствует?).
Но логически наложенный порядок построения должен отличаться от случая нормального (не- -виртуальное) наследование. Рассмотрим пример 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.
Крис Лэттнер написал на форумах разработчиков:
Это функция, которую мы намеренно не хотим поддерживать. Существует множество вещей, которые вызовут равенство функций указателя (в смысле системы быстрого типа, которое включает в себя несколько видов замыканий) для отказа или изменения в зависимости от оптимизации. Если для функций были определены «===», компилятору не разрешалось бы объединять идентичные тела методов, делиться ими и выполнять определенные оптимизации захвата при закрытии. Кроме того, равенство такого рода было бы чрезвычайно удивительным в некоторых контекстах дженериков, где вы можете получить кривые ревальментации, которые корректируют действительную подпись функции до той, которую ожидает тип функции.
blockquote>https://devforums.apple.com/message/1035180#1035180
Это означает, что вы даже не должны пытаться сравнивать замыкания для равенства, потому что оптимизация может повлиять на результат.
Вот одно возможное решение (концептуально такое же, как ответ «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
Я тоже искал ответ. И я нашел его наконец.
Что вам нужно - это фактический указатель на функцию и его контекст, скрытый в объекте функции.
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
). Но это должно быть достаточно хорошим.
Это отличный вопрос, и хотя Крис Лэттнер намеренно не хочет поддерживать эту функцию, я, как и многие разработчики, также не могу отпустить мои чувства, исходящие с других языков, где это тривиальная задача. Существует множество примеров 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)
для класса / поэтому я подал запрос функции , который нуждается в голосовании или объяснении, почему это плохая идея. Я также понимаю, что этот подход может быть плохим все вместе, если это так, может кто-нибудь объяснить, почему?
Хорошо, что прошло 2 дня, и никто не смог найти решение, поэтому я поменяю свой комментарий на ответ:
Насколько я могу судить, вы не можете проверить равенство или личность функций (например, ваш пример) и метаклассов (например, MyClass.self
):
Но - и это всего лишь идея - я не могу не заметить, что предложение where
в generics , по-видимому, способны проверять равенство типов. Может быть, вы можете использовать это, по крайней мере, для проверки личности?
Самый простой способ - обозначить тип блока как @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
Я много искал. Кажется, что нет никакого способа сравнения указателей функций. Лучшим решением, которое я получил, является инкапсуляция функции или закрытия в хешируемый объект. Например:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))