2015-04-18 15 views
6

Pracuję nad opracowaniem aplikacji w Swift. Chciałem zaprojektować system dla aplikacji, która pozwalałaby na luźne połączenie między obiektami, a jedną strategią (którą z powodzeniem stosowałem w innych językach) było stworzenie czegoś, co nazywam fabryką instancji. Jest to dość proste i tutaj jest podstawowym realizacja wpadłem w Swift:Alternatywna metoda ładowania w Swift

import Foundation 

private var typeGenerators = Dictionary<String, InstanceFactory.GeneratorCallback>() 

public class InstanceFactory: NSObject { 
    public typealias GeneratorCallback =() -> AnyObject! 

    public class func registerGeneratorFor(typeName: String, callback: GeneratorCallback) { 
     typeGenerators[typeName] = callback 
    } 

    public class func instanceOf(typeName: String) -> AnyObject! { 
     return typeGenerators[typeName]?() 
    } 
} 

Chodzi o to, że gdy instancja obiektu musi mieć dostęp do innej instancji obiektu, zamiast tworzenia że instancja wprost co mocniej para dwa obiekty, pierwszy obiekt odroczyłby fabrykę, aby dostarczyć potrzebną instancję, wywołując metodę instanceOf. Fabryka będzie wiedziała, jak zapewnić różne typy instancji, ponieważ te typy będą rejestrować się w fabryce i zapewnić zamknięcie, które może wygenerować instancję.

Sztuką jest to, jak uzyskać klasy, aby zarejestrować się w fabryce. Poprzednio zrobiłem podobną fabrykę w Objective-C, a sposób, w jaki dostałem rejestrację do pracy, polegał na zastąpieniu metody + obciążenia dla każdej klasy, która musiała zarejestrować się w fabryce. To zadziałało znakomicie dla Objective-C, i pomyślałem, że to może działać również dla Swifta, ponieważ ograniczałbym fabrykę tylko do dostarczania obiektów pochodzących z NSObject. Wyglądało na to, że mam to do roboty i poświęciłem dużo wysiłku na zaprojektowanie klas do wykorzystania w fabryce.

Po aktualizacji do Xcode 6.3 odkryłem, że Apple nie zezwala na stosowanie metody klasy obciążenia w Swift. Bez tego nie jestem świadomy mechanizmu umożliwiającego klasom automatyczne rejestrowanie się w fabryce.

Zastanawiam się, czy istnieje jakiś inny sposób na uruchomienie rejestracji.

Jakie alternatywy są dostępne, aby umożliwić rejestrację klas w fabryce lub jakie inne techniki można zastosować w celu uzyskania tego samego rodzaju luźnego połączenia, jakie zapewnia fabryka?

+0

Możesz kontynuować używanie Objective-C dla tej części ...? – matt

+0

Tak, mogę użyć do tego celu C. Z tego, co widzę, wadą byłoby wprowadzenie dodatkowych modułów dla każdej klasy, która musi zarejestrować się w fabryce, co może nie być tak dużym problemem. Oczywiście mogłem po prostu złożyć całą rejestrację w jedną metodę + obciążenia dla jednej klasy. Jednakże, jeśli to zrobię, równie dobrze mogę dokonać rejestracji w module Swift, jak delegat aplikacji. –

+0

Gdybym użył modułów mulitple, mógłbym nawet dokonać rejestracji w Objective-C++.Podejrzewam, że wszystkie moduły rejestracyjne działałyby tak samo, więc dobrze byłoby skonsolidować tę funkcjonalność. Nie przemyślałem tego całkowicie, ale wygląda na to, że ktoś może może stworzyć szablon C++ w Objective-C++, który może zawierać płytę kotła do rejestracji. Z drugiej strony prawdopodobnie nie ma zbyt wiele kodu, więc może nie jest to warte wysiłku. –

Odpowiedz

0

Rozważ skorzystanie z metody Swift za pomocą protokołu. Myślę, że rozwiązanie jest w rzeczywistości prostsze niż podejście Objective-C. Istnieją odmiany tego z ograniczeniami Self, które są jeszcze lepsze, jeśli masz większą kontrolę nad klasami.

// define a protocol to create an instance of a class 
protocol FactoryInstantiable { 
    static func makeFactoryInstance() -> AnyObject 
} 

// Factory for generating new instances 
public class InstanceFactory: NSObject { 
    public class func instanceOf(typeName: String) -> AnyObject! { 
     if let ProductType = NSClassFromString(typeName) as? FactoryInstantiable.Type { 
      return ProductType.makeFactoryInstance() 
     } else { 
      return nil 
     } 
    } 
} 


// your class which generally could be defined somewhere else 
class MyClass { 
    var counter : Int 

    init(counter: Int) { 
     self.counter = 0 
    } 
} 

// extension of your class to conform to the FactoryInstantiable protocol 
extension MyClass : FactoryInstantiable { 
    static func makeFactoryInstance() -> AnyObject { 
     return MyClass(counter: 0) 
    } 
} 
+1

Być może brakuje mi czegoś, jednak patrząc na proponowaną implementację, wydaje się, że pokonał on cały punkt InstanceFactory. Obiekt InstanceFactory umożliwia luźne połączenie między instancją klasy i użytkownikiem tej instancji. Przy pierwotnej implementacji, z pewnym rodzajem identyfikatora, fabryka może wytworzyć dowolną instancję. Użycie NSClassFromString pojawia się w celu ponownego wprowadzenia ścisłego sprzężenia, którego fabryka próbowała uniknąć, ponieważ NSClassFromString zapewnia tylko jedną klasę, która pasuje do identyfikatora łańcucha. –

+0

Mam trudności z zobaczeniem, jak ktoś może skonfigurować fabrykę, aby dostarczyć instancję wyprowadzoną, lub zezwolić fabryce na dostarczenie instancji dla określonego protokołu. Zastanawiam się, czy istnieje alternatywa dla NSClassFromString, która może być użyta zamiast tego. –

+0

O, widzę. Chcesz mieć niestandardowy identyfikator nazwy typu. W tym przypadku myślę, że jedyną opcją jest przypisywanie identyfikatorów do klas z zewnątrz (np. W swojej fabryce lub klasie pomocniczej), a nie z poziomu swoich klas, jak wspomniano w jednym z poprzednich komentarzy. Za każdym razem, gdy dodajesz nową klasę, musisz zmodyfikować klasę pomocnika, aby ją zarejestrować. Czy to nie jest lepsze niż oryginalne podejście do Celu-C? Przenosi rejestrację do jednego miejsca i pozwala dostosować, które klasy są zarejestrowane, do których identyfikatorów dla różnych przypadków użycia. –

2

Znalazłem możliwe rozwiązanie swojego problemu po Chciałem zarejestrować wszystkie ViewControllers które byłyby wykonawczych pewien protokół w mojej aplikacji i wpadłem na to pytanie i zarówno możliwą odpowiedź.

Oryginał został opublikowany tutaj: How to list all classes conforming to protocol in Swift?

ja przystosowane go Swift 3 i sprawiło, że nieco więcej Swift-y i ogólna:

import UIKit 

class ContextRoute: NSObject { 

} 

@objc protocol ContextRoutable { 
    static var route: ContextRoute { get } 
} 

class ContextRouter: NSObject { 
    private static var storedRoutes: [ContextRoute]? 
    static var routes: [ContextRoute] { 
     get { 
      if let storedRoutes = storedRoutes { 
       return storedRoutes 
      } 

      let routables: [ContextRoutable.Type] = classes(implementing: ContextRoutable.self) 
      let newRoutes = routables.map { routable in routable.route } 

      storedRoutes = newRoutes 

      return newRoutes 
     } 
    } 

    private class func classes<T>(implementing objcProtocol: Protocol) -> [T] { 
     let classes = classList().flatMap { objcClass in objcClass as? T } 

     return classes 
    } 

    private class func classList() -> [AnyObject] { 
     let expectedClassCount = objc_getClassList(nil, 0) 
     let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount)) 
     let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses) 
     let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount) 

     var classes = [AnyObject]() 
     for i in 0 ..< actualClassCount { 
      if let currentClass: AnyClass = allClasses[Int(i)], 
       class_conformsToProtocol(currentClass, ContextRoutable.self) { 
        classes.append(currentClass) 
      } 
     } 

     allClasses.deallocate(capacity: Int(expectedClassCount)) 

     return classes 
    } 
} 

próbowałem go w mojej aplikacji i działa. Zmieniłem ją w symulatorze i zajmuje ona 0,05 s dla aplikacji, która ma około 12000 klas.