Stellen Sie sich ein Buch vor, in dem es keine Unterteilung in Kapitel gibt und alles ohne logische und semantische Aufteilung verläuft, ein Buch, in dem es keine Absätze, keine Punkte und Kommas gibt, ein Buch, in dem die erste Zeile über eine Sache erzählt, die zweite über eine andere, die dritte noch einmal als erstes.
Präsentiert?
Könnten Sie verstehen, worum es in dem Buch geht?
Wie schnell könnten Sie die Stelle finden, an der Sie interessiert sind?
Ihr Code sowie der Inhalt des Buches müssen so strukturiert sein, dass der Code leicht zu lesen ist und die Bedeutung darin vermittelt.
In diesem Artikel werde ich Beispiele für das Organisieren von Code zeigen, in denen Klassen dieselbe Reihenfolge von Hauptblöcken und deren Aufteilung haben.
Der Einfachheit halber werde ich das Wort Klasse (Klasse) verwenden, aber jede Art von Typ (Klasse, Struktur, Aufzählung) implizieren.
Dank der Anwendung dieser Tipps wird Ihr Code lesbar, was in Zukunft Komfort und Schnelligkeit bei der Arbeit bietet.
Natürlich können die beschriebenen Tipps unter Beachtung der Grundprinzipien nach Ihren Wünschen aktualisiert werden.
Vergleichen wir zunächst denselben Code auf zwei Arten.
Ein Beispiel für eine unordentliche Klasse:
| final class MessyViewController: UIViewController { |
| |
| private let userService = UserService() |
| var userID: String? |
| private var userList: [User]? |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| |
| weak var delegate: SomeDelegate? |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| // Do any additional setup after loading the view. |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| @IBOutlet private weak var tableView: UITableView! |
| } |
Dieser Code ähnelt einem Speicherauszug von Methoden, Variablen und Outlets, in dem alles zusammengeführt wird. Es ist schwierig zu verstehen, worauf er sich bezieht und an welcher Stelle gesucht werden muss.
Ein Beispiel für eine reine Klasse:
| final class CleanViewController: UIViewController { |
| |
| // MARK: - IBOutlets |
| |
| @IBOutlet private weak var searchBar: UISearchBar! |
| @IBOutlet private weak var tableView: UITableView! |
| |
| // MARK: - Public Properties |
| |
| var userID: String? |
| weak var delegate: SomeDelegate? |
| |
| // MARK: - Private Properties |
| |
| private let userService = UserService() |
| private var userList: [User]? |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| } |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| // MARK: - IBActions |
| |
| @IBAction private func cancelButtonPressed(_ sender: UIBarButtonItem) { |
| dismiss(animated: true, completion: nil) |
| } |
| |
| } |
Leere Zeile 38 - Einrückung einer Zeile von der letzten Methode, damit Sie sehen können, wo die letzte schließende Klammer der Methode endet und wo die Klasse endet.
In beiden Beispielen wird die gleiche Funktionalität gezeigt, aber der Unterschied besteht darin, dass die zweite Option eine klare Struktur aufweist, aufgrund derer die Logik offensichtlicher ist, der Code leicht zu lesen ist, Sie schnell finden können, wonach Sie suchen, und außerdem ist es einfach schön, ihn anzusehen.
Grundprinzipien für die Bildung einer reinen Klassenstruktur:
- Verwenden Sie immer // MARK: -
- Geben Sie die Namen der Etiketten an und legen Sie deren Priorität fest
- Logik aus Lebenszyklusmethoden in separate Methoden umwandeln
- Wir verwenden die Erweiterung , um die Protokolle zu implementieren
- Wählen Sie Logisch verwandte Elemente
- Wir entfernen unbenutzte
- Routine automatisieren
1. Verwenden Sie immer // MARK: -
Um das Lesen zu vereinfachen, ist das Buch in Kapitel unterteilt, und es ist für uns angenehmer zu arbeiten, wenn wir mit
// MARK: - ein Klasseninhaltsverzeichnis erstellen.
Diese Bezeichnung hebt sich nicht nur gut vom gesamten Code ab, sondern erstellt auch automatisch ein Inhaltsverzeichnis. Sie hebt Abschnitte im Code in der Liste der Elemente dieser Datei fett hervor.
Sie können das Inhaltsverzeichnis einer Datei anzeigen, indem Sie auf die Schaltfläche nach dem Rechtspfeil (>) ganz oben in der Datei nach dem Namen dieser Datei oder Strg + 6 (Menü Dokumentelemente) klicken.2. Wir geben Namen von Labels an und legen deren Reihenfolge fest
Nachfolgend finden Sie die wichtigsten Bezeichnungen zum Aufteilen des Codes in logisch verbundene Blöcke und deren Reihenfolge:
| // MARK: - IBOutlets |
| |
| // MARK: - Public Properties |
| |
| // MARK: - Private Properties |
| |
| // MARK: - Initializers |
| |
| // MARK: - Lifecycle |
| |
| // MARK: - Public Methods |
| |
| // MARK: - Private Methods |
| |
| // MARK: - IBActions |
Wenn Sie diese Gruppierungsmethode verwenden, können Sie problemlos im Code einer beliebigen Klasse navigieren.
3. Ziehen Sie die Logik aus den Lebenszyklusmethoden in separate Methoden
Die Logik in den Lebenszyklusmethoden des ViewControllers muss in separate Methoden unterteilt werden, auch wenn Sie eine Methode mit einer Codezeile erstellen müssen. Heute ist eins und morgen ist zehn.
| ❌ NOT Preferred |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| navigationController?.navigationBar.backgroundColor = .red |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| navigationItem.title = "Some" |
| print("Some") |
| } |
| |
| |
| ✅ Preferred |
| |
| // MARK: - Lifecycle |
| |
| override func viewDidLoad() { |
| super.viewDidLoad() |
| |
| setupNavigationBar() |
| setupSomeButton() |
| printSome() |
| } |
| |
| |
| // MARK: - Private Methods |
| |
| private func setupNavigationBar() { |
| navigationController?.navigationBar.backgroundColor = .red |
| navigationItem.title = "Some" |
| } |
| |
| private func setupSomeButton() { |
| someButton.layer.cornerRadius = 10 |
| someButton.layer.masksToBounds = true |
| } |
| |
| private func printSome() { |
| print("Some") |
| } |
Aufgrund der Tatsache, dass Implementierungsdetails an Methoden von Drittanbietern ausgelagert werden, wird die Logik des Lebenszyklus klarer.
4. Verwenden Sie die Erweiterung, um Protokolle zu implementieren
Nehmen Sie die Protokollimplementierung in der mit
// MARK gekennzeichneten Erweiterung heraus
: - SomeProtocol :
| ❌ NOT Preferred |
| |
| final class CleanViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { |
| |
| // all methods |
| } |
| |
| |
| ✅ Preferred |
| |
| final class CleanViewController: UIViewController { |
| |
| // class stuff here |
| |
| } |
| |
| |
| // MARK: - Table View Data Source |
| extension CleanViewController: UITableViewDataSource { |
| |
| func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| |
| return userList?.count ?? 0 |
| } |
| |
| func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { |
| |
| let cell = UITableViewCell() |
| return cell |
| } |
| |
| } |
Dieses Etikett enthält alles, was mit diesem Protokoll zu tun hat - alles, was nur hier ist und nirgendwo anders hingehen muss, da sonst die Methoden und Eigenschaften des Protokolls über die Klasse verteilt sind.
5. Wählen Sie die logisch verwandten Elemente aus
Um die Sichtbarkeit zu erhöhen, müssen logisch verwandte Elemente mit einer leeren Zeile ausgewählt werden:
| ❌ NOT Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| loadingView.center = viewController.view.center |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| activityIndicator.startAnimating() |
| } |
| |
| |
| ✅ Preferred |
| |
| private func showActivityIndicator(on viewController: UIViewController) { |
| activityIndicator.center = viewController.view.center |
| activityIndicator.hidesWhenStopped = true |
| activityIndicator.style = .whiteLarge |
| |
| loadingView.center = viewController.view.center |
| loadingView.backgroundColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1) |
| loadingView.alpha = 0.5 |
| loadingView.clipsToBounds = true |
| loadingView.layer.cornerRadius = 15 |
| |
| viewController.view.addSubview(loadingView) |
| viewController.view.addSubview(activityIndicator) |
| |
| activityIndicator.startAnimating() |
| } |
6. Wir entfernen unbenutzte
Hinterlassen Sie keine unnötigen Kommentare (Standard), leeren Methoden oder toten Funktionen - dies verstopft den Code. Achten Sie auf die AppDelegate-Klasse. Höchstwahrscheinlich finden Sie dort leere Methoden mit Kommentaren.
| ❌ NOT Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| // Override point for customization after application launch. |
| return true |
| } |
| // |
| // func someFunc() { |
| // print("Some") |
| // } |
| |
| func applicationWillResignActive(_ application: UIApplication) { |
| // Sent when the application is about to move from active to inactive state. This can occur for certain |
| //types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits |
| //the application and it begins the transition to the background state. |
| // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. |
| } |
| |
| |
| ✅ Preferred |
| |
| @UIApplicationMain |
| class AppDelegate: UIResponder, UIApplicationDelegate { |
| |
| var window: UIWindow? |
| |
| |
| func application( |
| _ application: UIApplication, |
| didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { |
| |
| return true |
| } |
| |
| } |
7. Routine automatisieren
Verwenden Sie
Code Snippet, um ein manuelles Schreiben in jeder Klasse zu vermeiden.
// MARK: - SomeMark .
Wir schreiben ein Etikett, wählen es aus, dann Editor -> Code-Snippet erstellen, geben ihm einen Namen und nennen es Verknüpfung.// MARK: - Bonus
- Markieren Sie die Klasse mit dem Schlüsselwort final, wenn diese Klasse keine untergeordneten Elemente hat. Das Projekt wird schneller kompiliert und der Code wird schneller ausgeführt.
- Markieren Sie Eigenschaften, Outlets und Methoden mit dem privaten Schlüsselwort. Sie sind nur innerhalb der Klasse verfügbar und nicht in der öffentlichen Liste der Eigenschaften und Methoden enthalten, wenn sie dort nicht benötigt werden.
Ich wünsche Ihnen viel Erfolg bei der Anwendungsentwicklung und lasse Ihre Klasse sauberer werden!
// MARK: - Hilfe beim Schreiben eines ArtikelsSergey Pchelyakov
Alexey Pleshkov
AlekseyPleshkov// MARK: - LinksRay wenderlich Code-Stil