pisanie kodu danych podstawowych z myślą o wydajności pomaga przygotować aplikację na przyszłość. Twoja baza danych może być na początku mała, ale może łatwo rosnąć, co powoduje powolne zapytania i zmniejszone doświadczenie dla użytkownika.
odkąd zacząłem pisać Collect przez WeTransfer aplikacji w 2017 pisałem wiele podstawowych danych związanych z kodem, dotykając go prawie codziennie. Ponieważ miliony użytkowników dodają wiele treści, wykonywanie kodu związanego z podstawowymi danymi stało się ważną umiejętnością w naszym zespole.
na przestrzeni lat opracowaliśmy wiele spostrzeżeń, którymi z przyjemnością podzielę się z wami poprzez 5 wskazówek, które powinniście znać.
- 1: Użyj kontekstu obiektów zarządzanych w tle
- ważne: nie przekazuj instancji nsmanagedobject między kolejkami
- 2: tylko w razie potrzeby Zapisz kontekst obiektu zarządzanego
- rozważ dokładnie, kiedy zapisać zmiany
- 3: Pobierz tylko to, czego potrzebujesz
- 4: Użyj limitów pobierania
- 5: Usuń wiele obiektów naraz za pomocą NSBatchDeleteRequest
- 6: dowiedz się, jak debugować Kod podstawowych danych
- podsumowanie
1: Użyj kontekstu obiektów zarządzanych w tle
jedną rzeczą, której od początku nie robiliśmy, jest wykorzystanie kontekstu obiektów zarządzanych w tle. Użyliśmy kontekstu widoku tylko do wykonywania podstawowych zadań związanych z danymi: wstawianie nowej zawartości, usuwanie zawartości, pobieranie zawartości itp.
na początku nasza aplikacja była stosunkowo niewielka. Korzystanie tylko z kontekstu widoku nie było tak naprawdę problemem i nie skutkowało widocznymi karami wydajności związanymi z podstawowymi danymi. Oczywiście, gdy nasza aplikacja zaczęła się rozwijać, zdaliśmy sobie sprawę, że kontekst widoku jest związany z kolejką główną. Powolne zapytania zablokowały nasz interfejs użytkownika, a nasza aplikacja stała się mniej reagująca.
ogólnie rzecz biorąc, najlepszą praktyką jest wykonywanie przetwarzania danych w kolejce w tle, ponieważ może to być procesor intensywny. Przykłady takie jak importowanie JSON do podstawowych danych mogą w przeciwnym razie blokować kontekst widoku i powodować brak odpowiedzi w interfejsie użytkownika.
rozwiązaniem jest wykorzystanie kontekstu obiektu zarządzanego w tle. Najnowsze interfejsy API ułatwiają tworzenie nowego kontekstu z trwałego kontenera:
let backgroundContext = persistentContainer.newBackgroundContext()
polecam tę metodę nad inicjalizatorem NSManagedObjectContext(concurrenyType:)
, ponieważ zostanie ona automatycznie skojarzona z NSPersistentStoreCoordinator
i zostanie ustawiona na zużywanie NSManagedObjectContextDidSave
również. Dzięki temu kontekst tła jest zsynchronizowany z kontekstem widoku.
możesz zapisać ten kontekst tła na niestandardowej podklasie kontenera. W ten sposób możesz ponownie wykorzystać kontekst tła i zarządzać tylko dwoma kontekstami. Dzięki temu podstawowa struktura danych jest łatwa do zrozumienia i zapobiega wielu kontekstom synchronizacji.
jeśli musisz użyć kontekstu tła tylko w kilku miejscach, możesz również zdecydować się na użycie metody performBackgroundTask(_:)
, która tworzy kontekst tła w miejscu:
persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}
jednak ta metoda tworzy nową NSManagedObjectContext
za każdym razem, gdy jest wywoływana. Możesz rozważyć użycie współdzielonego kontekstu tła, jeśli wysyłasz go częściej do kontekstu tła.
ważne: nie przekazuj instancji nsmanagedobject między kolejkami
pisanie wielowątkowego kodu danych rdzeniowych jest o wiele bardziej złożone niż używanie pojedynczego kontekstu widoku. Powodem tego jest to, że nie można po prostu przekazać instancji NSManagedObject
z kontekstu widoku do kontekstu tła. Spowodowałoby to awarię i potencjalne uszkodzenie danych.
gdy konieczne jest przeniesienie zarządzanego obiektu z jednej kolejki do drugiej, możesz skorzystać z NSManagedObjectID
, która jest bezpieczna dla wątków:
let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}
2: tylko w razie potrzeby Zapisz kontekst obiektu zarządzanego
zapisanie kontekstu obiektu zarządzanego zatwierdza wszystkie bieżące zmiany w magazynie nadrzędnym kontekstu. Jak można sobie wyobrazić, nie jest to tania operacja i powinna być używana tylko w razie potrzeby, aby zapewnić wydajność w podstawowych danych.
po pierwsze, ważne jest, aby sprawdzić, czy jest w ogóle coś do zapisania. Jeśli nie ma zmian do zatwierdzenia, nie ma również powodu, aby zapisać. Tworząc metodę saveIfNeeded
pozwalasz sobie na łatwe wbudowanie sprawdzania:
rozważ dokładnie, kiedy zapisać zmiany
oprócz używania saveIfNeeded
zamiast save()
musisz również rozważyć, czy zapis ma sens. Chociaż kontekst może mieć zmiany, nie zawsze jest to konieczne do bezpośredniego zatwierdzania tych zmian.
na przykład, jeśli masz ważne wiele elementów do bazy danych, możesz zapisać je dopiero po zaimportowaniu wszystkich elementów w kontekście tła. Po zapisie często następują aktualizacje interfejsu użytkownika, a wielokrotne zapisywanie po sobie może z łatwością spowodować niepotrzebne przeładowanie. Poza tym, należy wziąć pod uwagę, że zapisane zmiany w kontekście tła są scalane z kontekstem widoku, blokując również kolejkę główną. Dlatego bądźcie świadomi!
3: Pobierz tylko to, czego potrzebujesz
pobieranie danych jest kosztownym zadaniem i musi być jak najbardziej wydajne, aby Twoja aplikacja była przygotowana na duże zbiory danych. Poniższy kod jest często popełnianym błędem:
ten kod załaduje wszystkie wstawione obiekty do pamięci, podczas gdy jest filtrowany bezpośrednio po, aby pozostać tylko z zawartością o nazwie.
znacznie wydajniej jest używać predykatów do pobierania tylko potrzebnych obiektów. Powyższy filtr można zapisać w następujący sposób NSPredicate
:
ma to dwie zalety:
- tylko potrzebne obiekty są ładowane do pamięci
- nie musisz iterować nad wszystkimi obiektami
predykaty są bardzo elastyczne i powinny umożliwiać pobranie żądanego zestawu danych w większości przypadków przy zachowaniu wydajności danych podstawowych.
4: Użyj limitów pobierania
kontynuując poprzedni przykład, ważne jest, aby ustawić limity pobierania, gdy chcesz wyświetlić tylko część zestawu danych.
na przykład powiedz, że potrzebujesz tylko pierwszych 3 nazw wszystkich elementów zawartości. W takim przypadku nie byłoby potrzeby wczytywania do pamięci wszystkich elementów zawartości o nazwie. Możemy temu zapobiec, ustawiając limit pobierania:
ten kod zwróci tylko pierwsze 3 elementy zawartości o nazwie.
5: Usuń wiele obiektów naraz za pomocą NSBatchDeleteRequest
zamiast iteracji nad zbiorem danych usuwając każdy obiekt jeden po drugim, często bardziej wydajne jest użycie NSBatchDeleteRequest
, która działa szybciej, ponieważ działa na poziomie SQL w samym trwałym sklepie.
możesz dowiedzieć się więcej o żądaniach usuwania partii w moim poście na blogu, używając NSBatchDeleteRequest do usuwania partii w podstawowych danych.
6: dowiedz się, jak debugować Kod podstawowych danych
podobnie jak w przypadku całego kodu, który piszesz, ważne jest, aby wiedzieć, jak zoptymalizować i debugować go, gdy nie działa zgodnie z oczekiwaniami. Istnieje wiele sposobów debugowania, które są najlepiej wyjaśnione w moim dedykowanym wpisie na blogu: debugowanie danych podstawowych w Xcode przy użyciu argumentów uruchamiania.
podsumowanie
pisanie wydajnego kodu podstawowych danych od samego początku pomaga przygotować aplikację do przyszłych dużych zbiorów danych. Chociaż aplikacja może działać na początku, może łatwo zwolnić, gdy baza danych i model rosną. Korzystając z kontekstu tła, inteligentnych żądań pobierania i grupowych żądań usuwania, sprawiasz, że kod podstawowych danych jest już bardziej wydajny.
jeśli chcesz jeszcze bardziej udoskonalić swoją wiedzę Swift, sprawdź stronę kategorii Swift. Zapraszam do kontaktu ze mną lub tweet do mnie na Twitterze, jeśli masz jakieś dodatkowe wskazówki lub opinie.
dzięki!