swift - program - xcode macstore




هل من الممكن تلبية بروتوكول سويفت وإضافة الحجج المتعثرة؟ (3)

إذا كان أي شخص لا يزال يبحث عن إجابة على هذا ، فقد ساعدني هذا الرابط:

https://oleb.net/blog/2016/05/default-arguments-in-protocols/

يتضمن تعريف الوظيفة الأصلي أساسًا كل المعلمات التي تحتاجها:

protocol Messaging {
    func sendMessage(message: String, count: Int)
}

ويوفر الملحق الخاص بك القيم الافتراضية ، استدعاء وظيفة البروتوكول الأصلي الخاص بك مع تلك القيم الافتراضية:

extension Messaging {
    func sendMessage(message: String, count: Int = 1) {
        sendMessage(message, count)
    }
}

إذا كان لديك بروتوكول مثل هذا:

protocol Messaging {
    func sendMessage(message: String)
}

هل هناك أي طريقة لإرضائها في فصل كالتالي:

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {}
}

سيكون من الجميل أن يكون ذلك ، حيث أن توقيع البروتوكول الناتج يكون راضيًا عن طريق إضافة المعلمة الافتراضية. هل هناك أي طريقة للحصول على هذا للعمل مع Swift 2؟

هذا مثال مبسط. دعنا نقول ، من أجل الحجة ، أن البروتوكول ثابت. يمكن للحل فقط تحديث فئة Messager. هدفي هو أن تكون قادرًا على الاتصال sendMessage() مثل ذلك:

let m: Messaging = Messager()
m.sendMessage("")

الطريقة الوحيدة التي وجدت فيها لإنجاز هذا (وإرضاء المترجم) هي التحميل الزائد مثل:

class Messager: Messaging {
    func sendMessage(message: String) {
        self.sendMessage(message, count: 1)
    }

    func sendMessage(message: String, count: Int = 1) {}
}

المشكلة في هذا النهج هي أن الإعدادات الافتراضية الخاصة بي يتم تحديدها في مكانين وفقد الميزة الرئيسية للمعلمات الافتراضية لـ Swift.


في Swift 3 ، يمكنك استخدام الامتدادات لحل ذلك ، ولكن قبيحًا بعض الشيء. نأمل في حل أفضل في الإصدارات السريعة القادمة.

import UIKit

protocol TestProtocol {
    func testFunction(a:Int, b:Int?) -> String
}

extension TestProtocol
{
    func testFunction(a:Int, b:Int? = nil) -> String {
        return testFunction(a:a, b:b)
    }
}

class TestClass: TestProtocol
{
    func testFunction(a:Int, b:Int?) -> String {
        return "a:\(a), b:\(b)"
    }
}

func testit(testProtocol: TestProtocol) {
    print(testProtocol.testFunction(a:10)) // will print a:10, b:nil
    print(testProtocol.testFunction(a:10, b:20)) // will print a:10, b:Optional(20)
}

let t = TestClass()
testit(testProtocol: t)

يمكنك إضافة typealiases إلى البروتوكول الخاص بك لتمثيل أنواع وسيطات مختلفة محتملة في دالة مخطط البروتوكول .sendMessage . في المثال أدناه ، حددت صراحةً أن الوسيطة الثانية ليس لها اسم داخلي أو خارجي.

نظرًا لأن لديك اثنين من أنواع typealias ، يمكنك إما تنفيذ هذا المخطط كواحد باستخدام نوعين مختلفين ( Messenger في المثال أدناه) ، أو ببساطة التخلص من الوسيطة الثانية ( AnotherMessenger في المثال) كنوع من AnotherMessenger الفارغة () ذات القيمة الافتراضية () (يمكنك التفكير في هذا كنوع فارغ مع قيمة باطلة).

protocol Messaging {
    typealias T
    typealias U
    func sendMessage(message: T, _ _ : U)
}

/* Class where you make use of 2nd argument */
class Messager: Messaging {

    func sendMessage(message: String, _ count: Int) {
        print(message + "\(count)")
    }
}

/* Class where you ignore 2nd argument */
class AnotherMessager : Messaging {

    func sendMessage(message: String, _ _ : () = ()) {
        print(message)
    }
}

/* Tests */
var a = Messager()
a.sendMessage("Hello world #", 1)
// prints "Hello World #1"

var b = AnotherMessager()
b.sendMessage("Hello world")
// prints "Hello World"

هذا أقرب ما يمكن أن أجعلك تحاكي السلوك الذي تصفه في السؤال وفي التعليقات أدناه. سأترك الحلول البديلة أدناه في حال تمكنهم من مساعدة شخص آخر لديه نفس التفكير ولكن مع قيود تصميم أقل صعوبة.

خيار آخر: ليس بالضبط السلوك الذي تطالب به ، ولكن يمكنك استخدام إغلاق مجهول كمعلمة واحدة لوظيفة مخطط بروتوكولك ، حيث لا يأخذ هذا الإغلاق أي وسيطات ولكنه يعرض صفيفًا من Any كائنات ، والتي يمكنك الوصول إليها ومعالجتها وظيفة sendMessage الخاصة بك كما يحلو لك.

protocol Messaging {
    func sendMessage(@autoclosure messages: ()->[Any])
}

class Messager: Messaging {
    func sendMessage(@autoclosure messages: ()->[Any]) {
        for message in messages() {
            print(message, terminator: "")
        }
    }
}

var a = Messager()
a.sendMessage([String("Hello "), String("World "), String("Number "), Int(1)])
// prints "Hello World Number 1"

سيكون البديل الآخر هو الفصل بين المخططات الخاصة بـ sendMessage(..) في بروتوكول Messaging الخاص بك ، واحد مع واحد بدون count المعلمة الإضافي. يمكنك بعد ذلك إضافة تطبيقات افتراضية (وهمية) لكلتا هاتين الوظيفتين عبر تمديد بروتوكول Messaging . سوف يلتزم برنامج Messager sendMessage(..) ببروتوكول Messaging حتى بدون أي تنفيذ لـ sendMessage(..) فيه ؛ في عدم وجودها ، يتم استخدام التطبيقات الافتراضية. أخيرًا ، قم بإجراء تطبيق مفصل فقط لوظيفة sendMessage التي ترغب في استخدامها في الفصل الدراسي.

protocol Messaging {
    func sendMessage(message: String)
    func sendMessage(message: String, count: Int)
}

/* Extend blueprints with default dummy implementations */
extension Messaging {
    func sendMessage(message: String) { }
    func sendMessage(message: String, count: Int = 1) { }
}

class Messager: Messaging {
    func sendMessage(message: String, count: Int = 1) {
        print(message + "\(count)")
    }
}

var a = Messager()
a.sendMessage("Hello world #")
// prints "Hello World #1"

لاحظ أن مثيلات الفصل الدراسي sendMessage كلتا sendMessage كطريقة للفئة المتاحة ؛ واحد منهم يجري وظيفتك ، والآخر تنفيذ الافتراضي وهمية.

إجابة قديمة قبل التعديل فيما يتعلق بمعلمات أنواع مختلفة (سأتركها هنا لأنها يمكن أن تكون بديلاً ممكنًا في حالة وجود جميع المعلمات من نفس النوع)

أخيرًا ، يمكنك الاستفادة من المعلمات varadic :

protocol Messaging {
    func sendMessage(messages: String...)
}

class Messager: Messaging {
    func sendMessage(messages: String...) {
        for message in messages {
            print(message)
        }
    }
}

var a = Messager()

a.sendMessage("Hello", "World", "!")

تقبل المعلمة varadic قيمًا صفرية أو أكثر من النوع المحدد. يمكنك استخدام معلمة متغير لتحديد أنه يمكن تمرير المعلمة عددًا متباينًا من قيم الإدخال عند استدعاء الدالة. اكتب المعلمات المتنوعة بإدخال ثلاثة أحرف (...) بعد اسم نوع المعلمة.





swift2