বইটি বলেছে যে "ফাংশন এবং ক্লোজারগুলি হল রেফারেন্স টাইপ"। সুতরাং, উল্লেখগুলি সমান কিনা আপনি কীভাবে আবিষ্কার করবেন? == এবং === কাজ করে না।
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
বইটি বলেছে যে "ফাংশন এবং ক্লোজারগুলি হল রেফারেন্স টাইপ"। সুতরাং, উল্লেখগুলি সমান কিনা আপনি কীভাবে আবিষ্কার করবেন? == এবং === কাজ করে না।
func a() { }
let å = a
let b = å === å // Could not find an overload for === that accepts the supplied arguments
åরেফারেন্সে ডায়রিটিকের ব্যবহার aসত্যই আকর্ষণীয়। এখানে কোন কনভেনশন আপনি সন্ধান করছেন? (আমি আসলে জানি না আমি জানি না; তবে এটি সম্ভবত খুব শক্তিশালী হতে পারে, বিশেষত খাঁটি কার্যকরী প্রোগ্রামিংয়ে in)
উত্তর:
ক্রিস ল্যাটনার বিকাশকারী ফোরামে লিখেছেন:
এটি এমন একটি বৈশিষ্ট্য যা আমরা ইচ্ছাকৃতভাবে সমর্থন করতে চাই না। বিভিন্ন ধরণের জিনিস রয়েছে যা অপ্টিমাইজেশনের উপর নির্ভর করে ফাংশনগুলির (প্যারাফাইটি টাইপ সিস্টেমের অর্থে, যেখানে বিভিন্ন ধরণের ক্লোজার অন্তর্ভুক্ত) পয়েন্টার সমতা তৈরি করবে। যদি "===" ফাংশনগুলিতে সংজ্ঞায়িত করা হয়, তবে সংকলকটিকে অভিন্ন পদ্ধতিতে সংশ্লেষ করার, অংশগুলি ভাগ করার এবং ক্লোজারগুলিতে নির্দিষ্ট ক্যাপচার অপ্টিমাইজেশান সম্পাদনের অনুমতি দেওয়া হবে না। আরও কিছু জেনেরিক প্রসঙ্গে এই ধরণের সাম্যতা অত্যন্ত আশ্চর্যজনক হবে, যেখানে আপনি পুনরায় সংক্ষিপ্তকরণ পেতে পারেন যা ফাংশনের ধরণের প্রত্যাশা অনুযায়ী কোনও ফাংশনের আসল স্বাক্ষর সামঞ্জস্য করে।
https://devforums.apple.com/message/1035180#1035180
এর অর্থ হ'ল সমতার জন্য বন্ধের তুলনা করার চেষ্টাও আপনার করা উচিত নয় কারণ অপ্টিমাইজেশান ফলাফলকে প্রভাবিত করতে পারে।
আমি অনেক খোঁজাখুঁজি করেছি। ফাংশন পয়েন্টার তুলনার কোনও উপায় নেই বলে মনে হয়। আমার কাছে সবচেয়ে ভাল সমাধানটি হ'ল কোনও হ্যাশযোগ্য অবজেক্টে ফাংশনটি বন্ধ করা বা বন্ধ করা। পছন্দ:
var handler:Handler = Handler(callback: { (message:String) in
//handler body
}))
সবচেয়ে সহজ উপায় হ'ল ব্লক প্রকারটি হিসাবে চিহ্নিত করা যায় @objc_blockএবং এখন আপনি এটিকে যেকোনওবজেক্টে কাস্ট করতে পারেন যা এর সাথে তুলনীয় ===। উদাহরণ:
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
আমি উত্তরটিও খুঁজছিলাম। এবং আমি এটি শেষ পেয়েছি।
আপনার যা প্রয়োজন তা হ'ল আসল ফাংশন পয়েন্টার এবং এর প্রসঙ্গটি ফাংশন অবজেক্টে লুকানো।
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
মজার অংশটি হ'ল কীভাবে সুইফ্ট অবাধে সুইফব্লককে ওজব্লক-এ কাস্ট করে, তবুও বাস্তবে দুটি কাস্ট করা সুইফব্লক ব্লক সর্বদা ভিন্ন মান হতে পারে, তবে ওবজব্লকগুলি তা করে না। যখন আমরা সুইজব্লককে ওবজব্লক কাস্ট করি তখন তাদের সাথে একই জিনিস ঘটে, তারা দুটি পৃথক মান হয়ে যায়। সুতরাং, রেফারেন্স সংরক্ষণ করার জন্য, এই ধরণের castালাই এড়ানো উচিত।
আমি এখনও এই পুরো বিষয়টি বুঝতে পেরেছি, তবে একটি জিনিস যা আমি ইচ্ছা করে রেখেছিলাম তা হল @convention(block)শ্রেণি / কাঠামো পদ্ধতিতে ব্যবহারের দক্ষতা , তাই আমি একটি বৈশিষ্ট্য অনুরোধ দায়ের করেছি যাতে আপ-ভোটিং প্রয়োজন বা এটি কেন খারাপ ধারণা তা ব্যাখ্যা করার জন্য request আমি একটি ধারণাও পেয়েছি যে এই পদ্ধতিটি সমস্ত একসাথে খারাপ হতে পারে, যদি তাই হয় তবে কেন কেউ ব্যাখ্যা করতে পারেন?
Struct S { func f(_: Int) -> Bool }, আপনার আসলে টাইপ S.fআছে টাইপ একটি ফাংশন আছে (S) -> (Int) -> Bool। এই ফাংশনটি করতে ভাগ করা। এটি সম্পূর্ণরূপে এর সুস্পষ্ট পরামিতি দ্বারা প্যারামিটারাইজড। আপনি যখন এটি একটি উদাহরণ পদ্ধতি হিসাবে ব্যবহার করেন (হয় selfকোনও বস্তুর উপর পদ্ধতিটি কল দিয়ে S().fস্পষ্ট করে প্যারামিটারকে আবদ্ধ করে , উদাহরণস্বরূপ , বা স্পষ্টভাবে এটি আবদ্ধ করে, যেমন S.f(S())) আপনি একটি নতুন ক্লোজার অবজেক্ট তৈরি করেন। এই অবজেক্টটি একটি পয়েন্টার সঞ্চয় করে S.f(যা ভাগ করা যায়) , but also to your instance (স্ব , the এস () stores)।
S। যদি ক্লোজার পয়েন্টার সমতাটি সম্ভব হত, তবে আপনি অবাক হয়ে অবাক হবেন যে s1.fএটি একই পয়েন্টার নয় s2.f(কারণ একটি ক্লোজার অবজেক্ট যা রেফারেন্স s1এবং fঅন্যটি ক্লোজার অবজেক্ট যা রেফারেন্স s2এবং f)।
এখানে একটি সম্ভাব্য সমাধান রয়েছে (ধারণাগতভাবে 'টিউনচে' উত্তর হিসাবে একই)। মুল বক্তব্যটি এমন কোনও শ্রেণীর সংজ্ঞা দেওয়া যা কিছু কার্যকারিতা (যেমন কমান্ড) মোড়ানো:
দ্রুত:
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
জাভা:
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
ঠিক ২ দিন কেটে গেছে এবং কেউই সমাধানের বিষয়ে চেষ্টা করে নি, তাই আমি আমার মন্তব্যে একটি উত্তরে পরিবর্তন করব:
আমি যতদূর বলতে পারি, আপনি সমতা বা ফাংশনগুলির পরিচয় (আপনার উদাহরণের মতো) এবং মেটাচ্লাসগুলি (যেমন, MyClass.self) পরীক্ষা করতে পারবেন না :
তবে - এবং এটি কেবল একটি ধারণা - আমি সাহায্য করতে পারছি না তবে লক্ষ্য করুন যে whereজেনেরিকের ধারাটি ধরণের ধরণের সাম্যগুলি পরীক্ষা করতে সক্ষম হয়েছে বলে মনে হয়। সুতরাং আপনি অন্তত পরিচয় যাচাই করার জন্য, যে উত্তোলন করতে পারেন?
কোনও সাধারণ সমাধান নয়, তবে যদি কেউ শ্রোতার ধরণটি বাস্তবায়নের চেষ্টা করে থাকে তবে আমি রেজিস্ট্রেশনের সময় ফাংশনটির একটি "আইডি" ফিরিয়ে শেষ করেছি যাতে আমি এটি পরে নিবন্ধনবিহীন করতে ব্যবহার করতে পারি (যা মূল প্রশ্নের একধরণের কাজ) "শ্রোতা" কেস হিসাবে সাধারণত নিবন্ধন না করে সমতার জন্য ফাংশনগুলি যাচাই করতে আসে যা অন্যান্য উত্তরগুলির তুলনায় কমপক্ষে "তুচ্ছ" নয়)।
সুতরাং এর মতো কিছু:
class OfflineManager {
var networkChangedListeners = [String:((Bool) -> Void)]()
func registerOnNetworkAvailabilityChangedListener(_ listener: @escaping ((Bool) -> Void)) -> String{
let listenerId = UUID().uuidString;
networkChangedListeners[listenerId] = listener;
return listenerId;
}
func unregisterOnNetworkAvailabilityChangedListener(_ listenerId: String){
networkChangedListeners.removeValue(forKey: listenerId);
}
}
এখন আপনাকে কেবল key"রেজিস্টার" ফাংশন দ্বারা ফেরত সংগ্রহ করতে হবে এবং নিবন্ধনবিহীন অবস্থায় পাস করতে হবে।
আমার সমাধানটি ছিল ক্লাসে ফাংশন মোড়ানো যা এনএসবজেক্টকে প্রসারিত করে
class Function<Type>: NSObject {
let value: (Type) -> Void
init(_ function: @escaping (Type) -> Void) {
value = function
}
}
আমি জানি আমি এই প্রশ্নের উত্তর ছয় বছর দেরিতে দিচ্ছি, তবে আমি মনে করি এটি প্রশ্নের পিছনে প্রেরণার দিকে তাকানো উপযুক্ত। প্রশ্নকর্তা মন্তব্য করেছেন:
রেফারেন্স দ্বারা একটি অনুরোধের তালিকা থেকে ক্লোজারগুলি সরাতে সক্ষম না হয়ে তবে, আমাদের নিজস্ব র্যাপার ক্লাস তৈরি করা দরকার। এটি একটি টানা এবং এটি প্রয়োজন হবে না।
সুতরাং আমার ধারণা, প্রশ্নকর্তা কলব্যাকের তালিকাটি বজায় রাখতে চান, এটির মতো:
class CallbackList {
private var callbacks: [() -> ()] = []
func call() {
callbacks.forEach { $0() }
}
func addCallback(_ callback: @escaping () -> ()) {
callbacks.append(callback)
}
func removeCallback(_ callback: @escaping () -> ()) {
callbacks.removeAll(where: { $0 == callback })
}
}
তবে আমরা removeCallbackসেভাবে লিখতে পারি না , কারণ ==ফাংশনের জন্য কাজ করে না। (না হয় ===।)
আপনার কলব্যাক তালিকা পরিচালনা করার জন্য এখানে একটি আলাদা উপায়। থেকে কোনও রেজিস্ট্রেশন অবজেক্টটি ফিরিয়ে দিন addCallbackএবং কলব্যাক সরাতে নিবন্ধকরণ অবজেক্টটি ব্যবহার করুন। এখানে ২০২০ সালে, আমরা কম্বাইনের AnyCancellableনিবন্ধকরণ হিসাবে ব্যবহার করতে পারি ।
সংশোধিত এপিআই এর মত দেখাচ্ছে:
class CallbackList {
private var callbacks: [NSObject: () -> ()] = [:]
func call() {
callbacks.values.forEach { $0() }
}
func addCallback(_ callback: @escaping () -> ()) -> AnyCancellable {
let key = NSObject()
callbacks[key] = callback
return .init { self.callbacks.removeValue(forKey: key) }
}
}
এখন, আপনি যখন কলব্যাক যুক্ত করেন, removeCallbackপরে এটিকে পাস করার জন্য আপনার এটিকে প্রায় রাখার প্রয়োজন হয় না । কোন removeCallbackপদ্ধতি নেই। পরিবর্তে, আপনি সংরক্ষণ করুন AnyCancellable, এবং cancelকলব্যাক অপসারণ করতে এর পদ্ধতিটি কল করুন । আরও ভাল, আপনি যদি AnyCancellableকোনও উদাহরণ সম্পত্তিতে সঞ্চয় করেন তবে উদাহরণটি নষ্ট হয়ে গেলে এটি স্বয়ংক্রিয়ভাবে বাতিল হয়ে যাবে।
MyClass.self