มีวิธีในการสร้างคลาสนามธรรมในภาษา Swift หรือนี่เป็นข้อ จำกัด เช่นเดียวกับ Objective-C หรือไม่? ฉันต้องการสร้างคลาสนามธรรมเปรียบได้กับสิ่งที่ Java กำหนดเป็นคลาสนามธรรม
มีวิธีในการสร้างคลาสนามธรรมในภาษา Swift หรือนี่เป็นข้อ จำกัด เช่นเดียวกับ Objective-C หรือไม่? ฉันต้องการสร้างคลาสนามธรรมเปรียบได้กับสิ่งที่ Java กำหนดเป็นคลาสนามธรรม
คำตอบ:
ไม่มีคลาสนามธรรมใน Swift (เหมือนกับ Objective-C) ทางออกที่ดีที่สุดของคุณคือการใช้โปรโตคอลซึ่งเป็นเหมือนส่วนต่อประสาน Java
ด้วย Swift 2.0 คุณสามารถเพิ่มวิธีการใช้งานและการใช้งานคุณสมบัติที่คำนวณได้โดยใช้ส่วนขยายโปรโตคอล ข้อ จำกัด ของคุณเท่านั้นที่คุณไม่สามารถให้สมาชิกตัวแปรหรือค่าคงที่และไม่มีการจัดส่งแบบไดนามิก
ตัวอย่างของเทคนิคนี้จะเป็น:
protocol Employee {
    var annualSalary: Int {get}
}
extension Employee {
    var biweeklySalary: Int {
        return self.annualSalary / 26
    }
    func logSalary() {
        print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
    }
}
struct SoftwareEngineer: Employee {
    var annualSalary: Int
    func logSalary() {
        print("overridden")
    }
}
let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly
โปรดสังเกตว่านี่เป็นการให้ "abstract class" เช่นเดียวกับฟีเจอร์สำหรับ structs แต่คลาสสามารถใช้โปรโตคอลเดียวกันได้
โปรดสังเกตด้วยว่าทุกคลาสหรือโครงสร้างที่ใช้โพรโทคอลพนักงานจะต้องประกาศคุณสมบัติประจำปีประจำปีอีกครั้ง
สิ่งสำคัญที่สุดคือการแจ้งให้ทราบว่าไม่มีการจัดส่งแบบไดนามิก เมื่อlogSalaryมีการเรียกใช้งานอินสแตนซ์ที่จัดเก็บตามที่SoftwareEngineerเรียกใช้รุ่นที่ถูกแทนที่ของวิธีการ เมื่อlogSalaryถูกเรียกบนอินสแตนซ์หลังจากที่มันได้รับการโยนไปที่Employeeจะเรียกการดำเนินงานเดิม Software Engineer(มันไม่ได้แบบไดนามิกส่งไปยังรุ่นแทนที่แม้ว่าอินสแตนซ์เป็นจริง
สำหรับข้อมูลเพิ่มเติมตรวจสอบวิดีโอ WWDC ที่ยอดเยี่ยมเกี่ยวกับคุณสมบัตินั้น: การสร้างแอปที่ดีขึ้นด้วยประเภทค่าใน Swift
protocol Animal { var property : Int { get set } }. นอกจากนี้คุณยังสามารถออกจากชุดหากคุณไม่ต้องการให้ทรัพย์สินมี setter
                    func logSalary()การประกาศโปรโตคอลพนักงานพิมพ์ตัวอย่างสำหรับการโทรทั้งoverridden logSalary()นี่คือใน Swift 3.1 ดังนั้นคุณจะได้รับประโยชน์ของ polymorphism วิธีการที่ถูกต้องเรียกว่าในทั้งสองกรณี
                    โปรดทราบว่าคำตอบนี้มีเป้าหมายที่ Swift 2.0 ขึ้นไป
คุณสามารถบรรลุพฤติกรรมเดียวกันกับโปรโตคอลและส่วนขยายโปรโตคอล
ขั้นแรกให้คุณเขียนโปรโตคอลที่ทำหน้าที่เป็นส่วนต่อประสานสำหรับวิธีการทั้งหมดที่ต้องนำไปใช้ในทุกประเภทที่สอดคล้องกับมัน
protocol Drivable {
    var speed: Float { get set }
}
จากนั้นคุณสามารถเพิ่มพฤติกรรมเริ่มต้นให้กับทุกประเภทที่สอดคล้องกับมัน
extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}
Drivableตอนนี้คุณสามารถสร้างรูปแบบใหม่โดยการใช้
struct Car: Drivable {
    var speed: Float = 0.0
    init() {}
}
let c = Car()
c.accelerate(10)
ดังนั้นโดยทั่วไปคุณจะได้รับ:
Drivableการใช้งานspeedDrivable( accelerate)Drivable ไม่รับประกันอินสแตนซ์เนื่องจากเป็นเพียงโปรโตคอลแบบจำลองนี้มีลักษณะเหมือนลักษณะความเป็นจริงมากขึ้นซึ่งหมายความว่าคุณสามารถปฏิบัติตามโปรโตคอลจำนวนมากและใช้งานการเริ่มต้นของพวกเขาใด ๆ ในขณะที่มี superclass นามธรรมคุณถูก จำกัด ชั้นลำดับชั้นที่เรียบง่าย
UICollectionViewDatasourceยังไม่มีความเป็นไปได้ที่จะขยายโปรโตคอลบางตัวอย่างเช่น ฉันต้องการลบต้นแบบสำเร็จรูปทั้งหมดและห่อหุ้มด้วยโพรโทคอล / ส่วนขยายแยกต่างหากจากนั้นใช้ซ้ำหลายคลาส ในความเป็นจริงรูปแบบแม่แบบจะเหมาะที่นี่ แต่ ...
                    ฉันคิดว่านี่ใกล้เคียงที่สุดของ Java abstractหรือ C # abstract:
class AbstractClass {
    private init() {
    }
}
โปรดทราบว่าเพื่อให้privateตัวดัดแปลงทำงานคุณต้องกำหนดคลาสนี้ในไฟล์ Swift แยกต่างหาก
แก้ไข:ยังรหัสนี้ไม่อนุญาตให้ประกาศวิธีนามธรรมและบังคับให้ดำเนินการ
วิธีที่ง่ายที่สุดคือใช้การเรียกไปยังfatalError("Not Implemented")เมธอด abstract (ไม่ใช่ตัวแปร) บนส่วนขยายโปรโตคอล
protocol MyInterface {
    func myMethod() -> String
}
extension MyInterface {
    func myMethod() -> String {
        fatalError("Not Implemented")
    }
}
class MyConcreteClass: MyInterface {
    func myMethod() -> String {
        return "The output"
    }
}
MyConcreteClass().myMethod()
              (MyConcreteClass() as MyInterface).myMethod()แต่ใช้ได้! ที่สำคัญคือรวมmyMethodอยู่ในการประกาศโปรโตคอล; มิฉะนั้นสายขัดข้อง
                    หลังจากที่ฉันพยายามมาหลายสัปดาห์ในที่สุดฉันก็รู้ว่าจะแปลคลาสนามธรรม Java / PHP เป็น Swift:
public class AbstractClass: NSObject {
    internal override init(){}
    public func getFoodToEat()->String
    {
        if(self._iAmHungry())
        {
            return self._myFavoriteFood();
        }else{
            return "";
        }
    }
    private func _myFavoriteFood()->String
    {
        return "Sandwich";
    }
    internal func _iAmHungry()->Bool
    {
        fatalError(__FUNCTION__ + "Must be overridden");
        return false;
    }
}
public class ConcreteClass: AbstractClass, IConcreteClass {
    private var _hungry: Bool = false;
    public override init() {
        super.init();
    }
    public func starve()->Void
    {
        self._hungry = true;
    }
    public override func _iAmHungry()->Bool
    {
        return self._hungry;
    }
}
public protocol IConcreteClass
{
    func _iAmHungry()->Bool;
}
class ConcreteClassTest: XCTestCase {
    func testExample() {
        var concreteClass: ConcreteClass = ConcreteClass();
        XCTAssertEqual("", concreteClass.getFoodToEat());
        concreteClass.starve();
        XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
    }
}
อย่างไรก็ตามฉันคิดว่า Apple ไม่ได้ใช้คลาสที่เป็นนามธรรมเพราะโดยทั่วไปจะใช้รูปแบบของผู้รับมอบสิทธิ์ + โพรโทคอลแทน ตัวอย่างเช่นรูปแบบเดียวกันข้างต้นจะทำได้ดีกว่าเช่นนี้:
import UIKit
    public class GoldenSpoonChild
    {
        private var delegate: IStomach!;
        internal init(){}
        internal func setup(delegate: IStomach)
        {
            self.delegate = delegate;
        }
        public func getFoodToEat()->String
        {
            if(self.delegate.iAmHungry())
            {
                return self._myFavoriteFood();
            }else{
                return "";
            }
        }
        private func _myFavoriteFood()->String
        {
            return "Sandwich";
        }
    }
    public class Mother: GoldenSpoonChild, IStomach
    {
        private var _hungry: Bool = false;
        public override init()
        {
            super.init();
            super.setup(self);
        }
        public func makeFamilyHungry()->Void
        {
            self._hungry = true;
        }
        public func iAmHungry()->Bool
        {
            return self._hungry;
        }
    }
    protocol IStomach
    {
        func iAmHungry()->Bool;
    }
    class DelegateTest: XCTestCase {
        func testGetFood() {
            var concreteClass: Mother = Mother();
            XCTAssertEqual("", concreteClass.getFoodToEat());
            concreteClass.makeFamilyHungry();
            XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
        }
    }
ฉันต้องการรูปแบบเช่นนี้เพราะฉันต้องการใช้วิธีการบางอย่างร่วมกันใน UITableViewController เช่น viewWillAppear เป็นต้นสิ่งนี้มีประโยชน์หรือไม่
มีวิธีการจำลองคลาสนามธรรมโดยใช้โปรโตคอล นี่คือตัวอย่าง:
protocol MyProtocol {
   func doIt()
}
class BaseClass {
    weak var myDelegate: MyProtocol?
    init() {
        ...
    }
    func myFunc() {
        ...
        self.myDelegate?.doIt()
        ...
    }
}
class ChildClass: BaseClass, MyProtocol {
    override init(){
        super.init()
        self.myDelegate = self
    }
    func doIt() {
        // Custom implementation
    }
}
              อีกวิธีหนึ่งที่คุณสามารถใช้คลาสนามธรรมคือการบล็อก initializer ฉันทำแบบนี้แล้ว:
class Element:CALayer { // IT'S ABSTRACT CLASS
    override init(){ 
        super.init()
        if self.dynamicType === Element.self {
        fatalError("Element is abstract class, do not try to create instance of this class")
        }
    }
}
              ฉันพยายามทำให้เป็นWeatherนามธรรม แต่การใช้โปรโตคอลไม่เหมาะเนื่องจากฉันต้องเขียนinitวิธีเดียวกันซ้ำแล้วซ้ำอีก การขยายโพรโทคอลและการเขียนinitวิธีมีปัญหาโดยเฉพาะอย่างยิ่งเมื่อฉันใช้การNSObjectปฏิบัติตามNSCodingสอดคล้องกับ
ดังนั้นฉันจึงได้สิ่งนี้ขึ้นมาเพื่อความNSCodingสอดคล้อง:
required init?(coder aDecoder: NSCoder) {
    guard type(of: self) != Weather.self else {
        fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
    }
    // Initialize...
}        
สำหรับinit:
fileprivate init(param: Any...) {
    // Initialize
}
              ย้ายการอ้างอิงทั้งหมดไปยังคุณสมบัตินามธรรมและเมธอดของคลาสฐานไปยังการใช้งานส่วนขยายโปรโตคอลซึ่งข้อ จำกัด ของตัวเองไปยังคลาสพื้นฐาน คุณจะสามารถเข้าถึงวิธีการและคุณสมบัติทั้งหมดของคลาสฐาน นอกจากนี้การตรวจสอบคอมไพเลอร์ใช้วิธีนามธรรมและคุณสมบัติในโปรโตคอลสำหรับคลาสที่ได้รับ
protocol Commom:class{
  var tableView:UITableView {get};
  func update();
}
class Base{
   var total:Int = 0;
}
extension Common where Self:Base{
   func update(){
     total += 1;
     tableView.reloadData();
   }
} 
class Derived:Base,Common{
  var tableView:UITableView{
    return owner.tableView;
  }
}
              ด้วยข้อ จำกัด ที่ไม่มีการแจกจ่ายแบบไดนามิกคุณสามารถทำสิ่งนี้:
import Foundation
protocol foo {
    static var instance: foo? { get }
    func prt()
}
extension foo {
    func prt() {
        if Thread.callStackSymbols.count > 30 {
            print("super")
        } else {
            Self.instance?.prt()
        }
    }
}
class foo1 : foo {
    static var instance : foo? = nil
    init() {
        foo1.instance = self
    }
    func prt() {
        print("foo1")
    }
}
class foo2 : foo {
    static var instance : foo? = nil
    init() {
        foo2.instance = self
    }
    func prt() {
        print("foo2")
    }
}
class foo3 : foo {
    static var instance : foo? = nil
    init() {
        foo3.instance = self
    }
}
var f1 : foo = foo1()
f1.prt()
var f2 : foo = foo2()
f2.prt()
var f3 : foo = foo3()
f3.prt()