Mianem dynamic framework określna jest biblioteka, która zostanie podlinkowana do programu już w trakcie jego działania, zamiast standardowo w trakcie kompilacji. Apple wprowadziło takie rozwiązanie, aby zredukować czas uruchamiania aplikacji oraz zmniejszyć wykorzystanie zasobów urządzenia. Pierwszymi z brzegu przykładami takich bibliotek będą UIKit oraz Foundation, z których z całą pewnością zdarzyło Wam już skorzystać 😎. Od iOS w wersji 8 Apple pozwoliło na implementację zewnętrznych bibliotek dynamicznych.
Dynamiczne biblioteki zostaną załadowane da pamięci urządzenia dopiero w chwili, w której będą faktycznie potrzebne. Biblioteki statyczne są natomiast ładowane są do pamięci urządzenia w chwili startu aplikacji, co oczywiście negatywnie wpływa na czas jej uruchomienia. Redukcja wykorzystania zasobów urządzenia – określana jako „Reduce memory footprint” – oznacza zmiejszenie rozmiarów pliku samej aplikacji, jak również ograniczenie jej wpływu na pamięć urządzenia. Dynamic frameworks mogą również (tak samo jak biblioteki statyczne) przechowywać foldery z assetami, takim jak choćby pliki .nib lub zdjęcia.
Tak wygląda dodawanie biblioteki statycznej do pliku wykonywalnego aplikacji:
Biblioteka statyczna zostaje bezpośrednio wbudowana. Biblioteka dynamiczna dodawana jest natomiast w sposób przedstawiony na poniższej ilustracji:
W przypadku dynamic framework kod biblioteki ładowany jest do pamięci urządzenia dopiero w chwili, gdy po raz pierwszy chcemy z niej skorzystać. Plik wykonywalny aplikacji nie będzie nim obciążony. Może się oczywiście zdarzyć tak, że będziemy chcieli skorzystać z danej biblioteki już na starcie aplikacji i tym sam zostanie ona załadowana do pamięci już na samym początku. Nie zmienia to jednak faktu, że sam plik wykonywalny nie będzie zawierał w sobie kopii danej biblioteki.
Każdy plik wykonywalny zawiera listę bibliotek, z których dana aplikacja będzie korzystała. Biblioteki te dzielą się na wymagane oraz opcjonalne. Za ładowanie odpowiednich frameworków odpowiada z kolei inna biblioteka o nazwie dynamic loader – w skrócie dyld. To tyle teorii. Za chwilę zbudujemy wspólnie prostą aplikację, w której sprawdzimy jak to sprawdza się w praktyce 🙃.
Dyanimic frameworks w praktyce
Stwórzcie nowy projekt w Xcode. Nazwę możecie wybrać dowolną, ale proponuję DynamicFrameworksDemo. Jako język możecie wybrać Swift, a dla reszty ustawień możecie zostawić domyślną wartość:
Po uruchomieniu projektu w oknie nawigatora wybierzcie DynamicFrameworksDemo, następnie Bulid Phases i rozwińcie zakładkę Link Binary With Libraries. Za pomocą przycisku „+” dodajcie dwie zależności – AVFoundation.framework oraz CoreBluetooth.framework. Zmieniając wartość w kolumnie „Status” ustawcie bibliotekę CoreBluetooth jako opcjonalną:
Zbudujcie projekt na emulatorze za pomocą skrótu Cmd + B, ale jeszcze nie uruchamiajcie go na emulatorze. Po zakończeniu builda (nie powinno być żadnych błędów) w nawigatorze rozwińcie folder Products, a następnie kliknijcie prawym przyciskiem na pliku DynamicFrameworksDemo.app i wybierzcie „Show in finder”:
Ukaże Wam się folder, w którym przechowywany jest plik .ipa aplikacji. Kliknijcie na nim prawy przyciskiem i wybierzcie „Show Package Contents”. Zostanie Wam zaprezentowany folder zwierający wszystkie pliki wymagane do uruchomienia aplikacji:
Skorzystamy teraz z narzędzia o nazwie otool, które pozwala na sprawdzenie jakie biblioteki zostały podlinkowane do aplikacji. Otwórzcie okno terminala i wpiszcie poniższe polecenie, ale nie wciskajcie jeszcze enter:
Dodajcie spację i przeciągnijcie do okna terminala plik wykonywalny aplikacji (ten o nazwie DynamicFrameworksDemo). Okno terminala powinno wyglądać mniej więcej tak:
Naciśnijcie teraz enter i Waszym oczom powinien ukazać się widok podobny do poniższego:
Dzięki narzędziu otool uruchomionemu z flagą -L możemy podejrzeć listę dynamicznych bibliotek, które zostały dołączone do naszej aplikacji. Oprócz bibliotek, które dodaliśmy ręcznie na liście pojawiły się frameworki, które wykorzystywane są w każdej aplikacji iOS – Foundation oraz UIKit.
Tak wygląda ścieżka, w której przechowywane są dynamic frameworks:
1 |
/System/Library/Frameworks/ |
Musicie jednak pamiętać o tym, że powyższa ścieżka wskazuję lokalizację na urządzeniu, na którym aplikacja została uruchomiona. W tym przypadku będzie to wirtualna lokalizacja na emulatorze. O ile to właśnie na nim testujecie aplikację 🤓.
Jeszcze raz skorzystamy z narzędzia otool, ale tym razem zmienimy flagę na małe „l”. Pozwoli nam to na wyświetlenie poszczególnych instrukcji load, odpowiedzialnych za ładowanie bibliotek do pamięci urządzenia. Otwórzcie ponownie okno terminala i naciśnijcie strzałkę „do góry” na klawiaturze, aby przywołać ostatnio wykonaną instrukcje. Teraz wystarczy, że zamienicie flagę z wielkiego ‚L’ na małe ‚l’:
Naciśnijcie enter, a w terminalu powinien pojawić się widok podobny do poniższego (wersja skrócona):
Korzystając teraz ze skrótu Cmd + F wyszukajcie wzmianki o CoreBluetooth oraz AVFoundation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Load command 13 cmd LC_LOAD_WEAK_DYLIB cmdsize 96 name /System/Library/Frameworks/CoreBluetooth.framework/CoreBluetooth (offset 24) time stamp 2 Thu Jan 1 01:00:02 1970 current version 1.0.0 compatibility version 1.0.0 Load command 14 cmd LC_LOAD_DYLIB cmdsize 88 name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24) time stamp 2 Thu Jan 1 01:00:02 1970 current version 1452.23.0 compatibility version 300.0.0 |
Porównajcie sobie wartość pola cmd. Dla AVFoundation wartość ta będzie równa LC_LOAD_DYLIB. CoreBluetooth zostało natomiast oznaczone LC_LOAD_WEAK_DYLIB. Jak się pewnie domyślacie drugi przypadek oznacza, że dana biblioteka traktowana jest jako opcjonalna.
Kiedy oznaczanie biblioteki jako opcjonalnej może okazać się przydatne? Załóżmy mocno abstrakcyjną sytuację, w której piszemy aplikację przeznaczoną do współpracy z urządzeniami Bluetooth. Najniższa wspierana przez naszą aplikację wersja iOS to 10.0. Współpraca z biblioteką CoreBluetooth nie będzie więc problemem. Na pewnym etapie zdecydujemy jednak, że fajnie będzie dodać do naszej apki elementy bardzo popularnej ostatnio rozszerzonej rzeczywistości. Biblioteka ARKit wymaga jednak iOS’a przynajmniej w wersji 11. W takiej sytuacji CoreBluetooth dodamy jako framework wymagany, natomiast ARKit jako framework opcjonalny.
I to właściwie wszystko. Możecie spokojnie wyrzucić z dysku aplikację, na której pracowaliśmy, bo raczej nie będzie Wam już do niczego potrzebna 😅.
Słowo na drogę
Dynamics frameworks leżą u podstaw działania wszystkich aplikacji napisanych z myślą o iOS. Warto posiadać choćby podstawową wiedzę na temat ich działania. Na początku może wydawać się to mało intuicyjne, ale jak sami mogliście się przekonać, rozłożenie każdej z operacji na mniejsze fragmenty pozwala poukładać to sobie jakoś w głowie 🤯. Do następnego 🧐.
Bullshit, dynamiczne frameworki bardzo źle wpływają na czas ładowania aplikacji. Poczytaj https://blog.automatic.com/how-we-cut-our-ios-apps-launch-time-in-half-with-this-one-cool-trick-7aca2011e2ea
Artykuł ciekawy. Ale generalnie przypadek ten odnosi się do znacznej ilości bibliotek dynamicznych. Warto jednak faktycznie zwrócić na niego uwagę 😉