Kolejne informacje na temat iOS. W pierwszej części posta na razie tylko zarysowałem tematykę związaną z przechowywaniem danych, dodałem trochę wiecej szczegółów z komunikacji sieciowej oraz pojawiły się zagadnienia związane z życiem aplikacji, wykonywaniem w tle, wątkami, dispatcherem. W drugiej części wrzuciłem trochę uzupełnień do poruszanych już zagadnień.
Jakieś komentarze? Ogólnie … nic nowego mimo nowych informacji. Rozwiązania w iOS mimo czasami dość innej formy mają ideowo nieraz dużo wspólnego z rozwiązaniami Windows / Windows Phone. Parę bardziej szczegółowych uwag:
- system plików - izolacja aplikacji nie zaskakuje, tak jak predefiniowane foldery, backupowanie folderu przez system kojarzy mi się z nowo wprowadzonym folderem w WP 8.1
- NSUserDefaults - przechowywanie ustawień aplikacji też nie zaskakuje, natomiast mamy tu dostęp przez aplikację Settings, iOS ma generowany interfejs do ustawień tak jak Android, w Windows nie ma na razie tego.
- SQLite - lokalna baza wspólna dla Android, iOS i Windows/Windows Phone, nic nowego
- przechowywanie ustawień w chmurze - w Windows mamy ustawienia między maszynami trzymane też w chmurze
- CoreData - uniwersalna persystencja, czy w tym kierunku nie zmierza Entity Framework 7?
- GDC - czy zlecenia wykonania czegoś w tle/ w głównym wątku UI nie jest nam znane choćby z aplikacji XAML?
- parsowanie XML jest koszmarne, na szczęście dziś króluje JSON
- przechodzenie aplikacji do tła i usypianie - czy tego już nie znamy?
- kontynuacja pracy po przejściu do background: lokalne notyfikacje, kod w taskach ograniczonych czasowo , nasłuchiwanie zmian w geolokalizacji, sieci przez VOIP, kontynuacja odtwarzania audio, budzenie przez sprzętowe akcesoria, notyfikacje push - zaskoczenia raczej nie ma
Nihil novi sub sole…
Persystencja
Możliwości
- system plików
- NSUserDefaults
- SQLite
- iCloud - NSUbiquitousKeyValueStore
- CoreData
- z i bez SQLite
- iCloud z CoreData
Przechowywanie danych lokalnie
- każda aplikacja jest w piaskownicy
System plików dla aplikacji iOS
- <App_Home>/AppName.app - folder zawierający aplikację, nie należy do niego niczego zapisywać
- <App_Home>/Documents/ - dla danych krytycznych i plików użytkownika, folder backupowany przez system
- <App_Home>/Documents/Inbox - dla plików nieznanych (np. załączników)
- <App_Home>/Library/ - dla plików aplikacji i ustawień
- <App_Home>/tmp/ - dla plików tymczasowych
Ładowanie zasobów z paczki aplikacji
NSURL* url = [[NSBundle mainBundle] URLForResource: @”film” withExtension: @”mp4”];
Prosta serializacja
NSArray, NSDictionary i NSString mają metody do zapisywania i inicjalizowania z pliku. W Objective-C serializacja wielu typów jest bezpośrednio w nie wbudowana, nie ma używania obiektów o innych typach.
NSFileManager
- bardziej bezpośrednia kontrola nad plikami
- enumeracja folderów
- udostępniony protokół do notyfikowania o zmianach w pliku
NSUserDefaults
- ustawienia użytkownika (tylko małe ilości danych)
- XML dodawany do folderu aplikacji library
Settings bundle
- dodanie jako szablon z nowego pliku buduje automatycznie infrastrukturę dla
- wyświetlania ustawień w ustawieniach aplikacji
- lokalizacji nazw ustawień
Root.plist
- plik XML dodawany do projektu - metadane dla schematu ustawień i UI
- zorganizowany na grupy i elementy, możliwość tworzenia hierarchii
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
float zoom = [defaults floatForKey: @”zoom”];
Ustawienia aplikacji: ekran Home –> Settings –> nazwa aplikacji
SQLite
- dołączany do iOS
- możliwość dołączenia wcześniej utworzonej bazy jako zasób lub utworzenia i wypełnienia danymi, kiedy aplikacja pierwszy raz ładuje się
sqlite3 *contactDB;
NSString* nsdbpath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: @”baza.sqlitedb”];
const char *dbpath = [nsdbpath UTF8String];
if (sqlite3_open(dbpath, &contactDB) == SQLITE_OK)
{
const char* sql = “select * from documents;”;
sqlite3_stmt *selectstmt;
if (sqlite3_prepare_v2(contactDB, sql, –1, &selectstmt, NULL) == SQLITE_OK) {
NSInteger primaryKey;
while(sqlite3_step(selectstmt) == SQLITE_ROW) {
char *cNumber = sqlite3_column_text(selectstmt, 1);
}
}
}
iCloud
- możliwość współdzielenia danych pomiędzy urządzeniami
- włączamy w ustawieniach projektu
- słownik klucz-wartość (domyślna nazwa pliku - id paczki)
- także alokowanie folderów
CoreData
- “ORM” zapewniający automatyczną persystencję
- możliwość przechowywania w:
- pamięci
- bazie SQLite
- pliku binarnym
- możliwość synchronizowania z iCloud (tylko przy SQLite)
- przyrostowe aktualizacje
Komunikacja sieciowa
request
- NSURLRequest - wiecej w odc.2
- NSMutableURLRequest
- podklasa NSURLRequest
- używamy przy
- HTTP POST, PUT, DELETE
- zmianie nagłówków HTTP
NUSRL *url = [NSURL URLWithString: @”http://serwer/app/documents”];
NSMutableURLRequest* rurl = [NSMutableURLRequest requestWithURL: url];
[rurl setValue: @”application/json” forHTTPHeaderField: @”Accept”];
[rurl setHTTPMethod: @”GET”];
realizacja requestu
- NSURLConnection - pobranie do pamięci, używa obiektu NSURLRequest
- NSURLDownload - pobranie zasobu do pliku, też używa NSURLRequest
obsługa synchroniczna i asynchroniczna
asynchroniczna - inna nieco obsługa niż w odc.2
NSURLConnectionDataDelegate
- propercja delegate nie specyfikuje jawnie listy protokołów
conn = [[NSURLConnection alloc] initWithRequest: theRequest delegate: self];
- (NSCachedURLResponse *) connection: (NSURLConnection *) connection willCacheResponse: (NSCachedURLResponse *) cachedResponse {
return nil;
}
- (void) connection: (NSURLConnection *) connection didFailWithError: (NSError *) error {
done = YES;
[self performSelectorOnMainThread: @selector(parseError:) withObject: error waitUntilDone: NO];
}
- (void) connection: (NSURLConnection *) connection didReceiveData: (NSData *) data {
[xmlData appendData: data];
}
- (void) connectionDidFinishLoading: (NSURLConnection *) connection {
self.done = YES;
}
Grand Central Dispatch (GCD)
- przy starcie/przywracaniu aplikacja może zlecić pracę w tle
- zlecenie przez GCD wykonania bloki kodu (anonimowa funkcja: ^{ //kod } ) w tle i odesłanie wyniku do głównego wątku UI (aktualizacja UI podobnie jak w XAML może odbywać się tylko w głównym wątku)
[NSURLConnection sendAsynchronousRequest: req queue: [[NSOperationQueue alloc] init]
completionHandler: ^(NSURLResponse *response, NSData *data, NSError *err) {
NSDictionary* documentsData = [NSJSONSerialization JSONObjectWithData: data
options: NSJSONReadingAllowFragments error:nil];
NSArray* documents = [documentsData objectForKey: @”a”];
_objects = [NSArray arrayWithArray: documents];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}];
Deserializacja
- XML: NSXMLParser
- implementujemy protokół NSXMLParserDelegate
- JSON: NSJSONSerialization
parsowanie XML
NSMutableData *xmlData = [self getXMLData];
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:xmlData];
parser.delegate = self;
self.currentString = [NSMutableString string];
[parser parse];
- (void) parser: (NSXMLParser *) parser didStartElement: (NSString *) elementName
namespaceURI: (NSString *) namespaceURI qualifiedName: (NSString *) qualifiedName
attributes: (NSDictionary *) attributeDict {
if ([elementName isEqualToString: @”xxx”]) {
self.store = YES;
}
}
- (void) parser: (NSXMLParser *) parser foundCharacters: (NSString *) string {
if (store) [currentString appendString: string];
}
- (void) parser: (NSXMLParser *) parser didEndElement: (NSString *) elementName
namespaceURI: (NSString *) namespaceURI qualifiedName: (NSString *) qName {
self.store = NO;
}
Autentykacja
- można użyć NSMutableURLRequest i ustawić nagłówki dla autentykacji
- część protokołu NSURLConnectionDataDelegate
- przykład Apple “AdvancedURLConnections”
Czas życia aplikacji, wydajność, praca w tle, wątki, dispatcher
Czas życia aplikacji
- może zostać uśpiona, gdy użytkownik naciska przycisk home
- gdy użytkownik uruchamia ponownie aplikację, jeśli jest uśpiona zostanie przywrócona, zamiast uruchamiana
- aplikacja pozostaje uśpiona, gdy nie było dużych żądań odnośnie pamięci
- częstsze pozostawanie w uśpieniu i szybsze przywracanie osiągniemy ograniczając zużycie pamięci przez aplikację
- aplikacja nie powinna też zbyt dużo robić podczas przywracania
UIApplicationDelegate
- (void) applicationWillResignActive: (UIApplication *) application { … }
- (void) applicationDidEnterBackground: (UIApplication *) application { … }
- (void) applicationWillEnterForeground: (UIApplication *) application { … }
- (void) applicationDidBecomeActive: (UIApplication *) application { … }
- (void) applicationWillTerminate: (UIApplication *) application { … }
self.viewController.textView.text = @“…”;
Gdy drugi raz wejdziemy na ekran home z poziomu aplikacji, pojawi się na dole lista ostatnio używanych aplikacji. Aplikację możemy zakończyć naciskając na ikonie znak “-“, który pojawia się po jej naciśnięciu.
By aplikacja zostawała w pamięci należy:
- zredukować ilość wymaganej pamięci (poniżej 16 MB)
- NSCache API - eliminuje dane podczas przechodzenia do tła lub przy ostrzeżeniach dotyczących pamięci
- NSPurgableData - obiekty, które czyszczą swoją pamięć, jeśli nie są używane, dodanie ich do NSCache umożliwia temu ostatniemu czyszczenie takich obiektów w sposób bardziej agresywny
Narzędzia
- wizualny profiler dla iOS
- zużycie CPU, pamięci, zasobów graficznych (OpenGL ES & Core Animation), sieć, I/O, baterie
- obsługa emulatora i fizycznego urządzenia
- profilowanie niektórych rzeczy nie jest obsługiwane na emulatorze (np. grafika)
- wyniki są prezentowane w czasie rzeczywistym, mogą też zapisane i odtworzone później
- szablony dla popularnych tasków
- zintegrowane z XCode
- komenda: Run > Profile
- menu: XCode | Open Developer Tool
Wykonywanie w tle
Wiele sposobów w iOS na kontynuowanie wykonywania kodu po przeniesienie do tła aplikacji:
- lokalne notyfikacje - po wcześniejszym ich zaplanowaniu przez aplikację (np. timer dla gry lub czas do wylogowania)
- długo trwające taski - aplikacja może zaplanować do wykonania blok kodu, kiedy jest informowana, że przejdzie do background (w zależności od zasobów, ograniczenie czasowe do 10 min)
- odtwarzacz audio - aplikacja odtwarzająca pliki audio może kontynuować odtwarzanie po wysłaniu aplikacji do background
- serwisy lokalizacji - aplikacja może uruchomić serwis lokalizacji w tle i być obudzona przy zmianie lokalizacji przez użytkownika
- VoIP - aplikacja VoIP może zarejestrować się do okresowego budzenia, by sprawdzać przez sieć, czy ktoś nie dzwoni
- aplikacje kioskowe mogą być uruchamiane w tle lub budzone jeśli są już w tle, aby odpowiedzieć na notyfikację push i pobrać nową rzecz
- aplikacje, które potrzebują powiadomień od akcesoriów (np. Bluetooth) mogą się zarejestrować, by być notyfikowane, jeśli zewnętrzne urządzenie wyśle zdarzenie
Wątki
- NSThread
- najprostsze użycie - metoda NSObject performSelectorInBackground
- większa kontrola - bezpośrednio korzystamy z NSThread
- NSTimer
- potrafi zaplanować periodyczne taski
Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//długo wykonujący się task
…
dispatch_async(dispatch_get_main_queue(), ^{
//aktualizacja UI
});
}
Uzupełnienia
iOS7
- pierwsze większe przeprojektowanie UI
- nie tylko zmiany w UI
- przeprojektowanie aplikacji
- wsparcie dla 64-bit
- serwisy w tle
- …
Wersje w drugiej połowie 2013:
- 7 2013 80%
- 6 2012 17% (większość projektów)
- 5 2011 3% (w tym wszystkie starsze)
Możliwość warunkowej kompilacji na różne wersje lub wykrywania wersji w runtime.
Możliwość pisania aplikacji w C/C++
- programowanie niskopoziomowe
- dużo gier
Objective-C
- metoda new wywołująca alloc i init: [klasa new]
- import
- #import <UIKit/UIKit.h> - import frameworka
- #import “MyClass.h” - import źródła
- propercje mogą być zdefiniowane także w implementacji, wtedy są prywatne
- potrzebujemy używać @synthesize lub implementować propercje otrzymane od protokołu
- na iOS nie ma garbage collection
- popularne atrybuty propercji
- readonly
- readwrite
- nonatomic
- atomic
- assign - tylko proste przypisanie do zmiennej
- weak
- strong - inkrementuje licznik referencji
- protokół
- zbiór metod i propercji, opcjonalność jest domyślna
- #pragma mark MyProtocol protocol - przed metodami implementującymi protokół, lepsza nawigacja po implementacji klasy w XCode
- nie ma matematycznych operatorów dla NSNumber, trochę jest dla NSDecimalNumber
- formatery
- konwersje typów: string –> date, string –> number, date –> sformatowany string
- klasy: NSNumberFormatter, NSDateFormatter
- uproszczona inicjalizacja słownika: NSDictionary* dictionary = @{@”key1”: @”value1”, @”key2”: @”value2”}
- bloki mogą być prostszą implementacją delegatów niż protokoły
void (^block1)() = ^{
…
};
block1();
void (^block2)(NSString*) = ^(NSString* param) {
…
}
block2(@”Pro Forma”);
Uruchamianie aplikacji
Mogą być także uruchamiane przez inne aplikacje za pomocą uri (skojarzony z aplikacją prefix).
XCode
- workspace opcjonalny dla aplikacji iOS, często stosowany w aplikacjach MacOSX
- projekt domyślnie ma jeden target, ale możemy dodać dodatkowe (budowanie kilku aplikacji z jednego projektu ze współdzieleniem kodu)
- plik .xcassets - asset library, plik przechowujący grafikę oraz pozwalający nią zarządzać
- Capabilities - od XCode 5 np. Background modes
- 2 kompilatory/debuggery
- GCC/GDB - GCC zmodyfikowany do kompilacji Objective-C, usunięty w XCode 5.0
- LLVM/LLDB - nowszy i domyślny
- pliki nib
- serializacja dowolnego grafu obiektów Objective-C (nie tylko elementów wizualnych)
- są zależne od rozdzielczości, przy budowie uniwersalnej aplikacji trzeba tworzyć różne pliki dla iPhone i iPad
- runtime deserializuje plik do prawdziwych obiektów
- storyboard
- od XCode 4, alternatywa do pliku nib, w XCode 5 domyślnym plikiem dla UI w nowych projektach
- umożliwia używanie nieograniczonego canvas
- nie tylko widoki, ale także dane i sekwencje pomiędzy widokami
- tak jak w plikach nib potrzebujemy dedykowanych wersji dla konkretnych rozdzielczości urządzeń
- możliwość zapięcia się na kilka procesów jednocześnie na używanej maszynie, wielu urządzeniach, symulatorze iOS
- obsługa breakpointów, także ta bardziej zaawansowana, przypomina VS - definiowanie warunków, ilości powtórzeń, akcji (lepiej używać niż NSLog)
Brak komentarzy:
Prześlij komentarz