Статьи

Еще 10 «самых полезных» советов для iPhone (часть 2)

 Этот пост является продолжением моих 3 предыдущих постов:

Давай покатимся.

6. Восстановление состояния приложения.

Платформа Three20 может восстанавливать контроллеры представления, если вы придерживаетесь
TTNavigatorи передаете все данные в качестве параметров URL. Но представьте, что у вас есть сложный объект для передачи между представлениями, например, критерии поиска, или вы хотите сохранить электронную почту последнего успешного входа в систему. Вы можете объединить функциональность контроллеров представления Three20 с NSUserDefaultsклассом Apple
.

Когда приложение запускается и завершается, UIApplicationDelegateвызываются соответствующие
методы. Эти методы являются идеальными местами для восстановления / сохранения состояния приложения. Чтобы проиллюстрировать эту ситуацию, взгляните на следующий код:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // restore app's state from NSUserDefaults
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = [defaults objectForKey:@"searchCriteria"];
    NSKeyedArchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    SearchCriteria *searchCriteria = [[SearchCriteria alloc] initWithCoder: unarchiver];
    [DataContainerService setSearchCriteria:searchCriteria];

    NSString *lastSuccessfulLoginEmailAddress = [defaults objectForKey:@"lastSuccessfulLoginEmailAddress"];
    [DataContainerService setLastSuccessfulLoginEmailAddress:lastSuccessfulLoginEmailAddress];
    
    [TTStyleSheet setGlobalStyleSheet:[[[DefaultStyles alloc] init] autorelease]];
    
    TTNavigator* navigator = [TTNavigator navigator];
    navigator.persistenceMode = TTNavigatorPersistenceModeAll;
    navigator.supportsShakeToReload = YES;
    navigator.opensExternalURLs = YES;

    TTURLMap* map = navigator.URLMap;

    [map from:@"*" toViewController:[TTWebController class]];
    [map from:@"tt://tabBar" toSharedViewController:[TabBarController class]];
    [map from:@"tt://search" toSharedViewController:[SearchViewController class]];
    [map from:@"tt://map" toSharedViewController:[MapViewController class]];

    if (![navigator restoreViewControllers]) {
        [navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://tabBar"]];
    }
    
    return YES;
}

- (void)applicationWillTerminate:(UIApplication *)application {
    SearchCriteria *searchCriteria = [DataContainerService searchCriteria];
    
    NSMutableData *data = [NSMutableData new];
    NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [propertySearchCriteria encodeWithCoder:encoder];
    [encoder finishEncoding];
    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:data forKey:@"searchCriteria"];
    [data release];
    
    NSString *lastSuccessfulLoginEmailAddress = [DataContainerService lastSuccessfulLoginEmailAddress];
    [defaults setObject:lastSuccessfulLoginEmailAddress forKey:@"lastSuccessfulLoginEmailAddress"];

}

7. Используйте sizeToFit

Многие элементы управления пользовательского интерфейса (например
UILabel,
UIButton) предлагают функцию автоматического изменения размера. Это можно использовать при динамическом заполнении текста из JSON и т. Д. Чтобы изменить размер метки, просто вызовите
[label sizeToFit]метод, и вы дома.

8. Динамические кнопки со свойством userInfo

Иногда, в зависимости от дизайна представления, вы можете получить множество кнопок, выполняющих одно и то же действие, но с другим объектом.

Например, представьте список отделений для применения в больнице. Это как X динамически создаваемых кнопок с именами пациентов, когда вы нажимаете кнопку, появляется представление с данными пациента. Обычно вы назначаете всем кнопкам одно и то же действие, и внутри него вы бы перебирали коллекцию кнопок, находили индекс затронутой кнопки, извлекали пациента из коллекции пациента, а затем на основании идентификатора пациента открывали представление сведений.

Это может быть упрощено, если вы расширяете UIButtonи добавляете
userInfoсвойство , коллекции для пациентов и кнопок не требуются
. Взглянем:

@interface UserInfoButton : UIButton {
    id _userInfo;
}
@property (nonatomic, assign) id userInfo;
@end

@implementation UserInfoButton
@synthesize userInfo = _userInfo;
@end

И ввиду контроллера:

for (Patient *patient in patients) {
    UserInfoButton *button = [[UserInfoButton alloc] initWithFrame:CGRectMake(0, offset, 155, 15)];
    [button addTarget:self action:@selector(navigateToPatientDetailsView:) forControlEvents:UIControlEventTouchUpInside];
    button.userInfo = patient;
    ...
}


and in action:

-(void) navigateToPatientDetailsView: (UserInfoButton*) sender {
    TTOpenURL([NSString stringWithFormat:@"tt://patient/%d", ((Patient*)sender.userInfo).patientId]);
}

Просто, не правда ли?

9. Используйте JSON для обмена данными.

Если вы обмениваетесь данными с внешними веб-сервисами, гораздо лучше использовать RESTful Web Services и JSON.

Зачем? Существует две основные причины:

1. Веб-службы RESTful легко кэшируются как на стороне клиента, так и на стороне сервера.

2. Формат данных JSON очень компактен по сравнению с довольно подробным XML.

Существует множество библиотек JSON для Objective-C. Просто забери свой любимый.

10. Остерегайтесь автоматически выпущенных объектов, отпустите объект сразу после того, как вы закончите с ними работать

Автоподводимые объекты — это кошмары. Почему вы должны остерегаться их? Потому что они могут быть автоматически освобождены в любое время (если вы не сохраните их). Вы не обнаружите эту проблему при разработке приложения, она будет обнаружена, когда в системных или пользовательских приемочных тестах ваше приложение будет работать дольше нескольких секунд.

В большинстве случаев очень легко узнать, был ли объект, возвращенный методом, автоматически освобожден или нет, так как Apple имеет соглашение об именах, которое говорит вам об этом. Если метод создает объект и его имя начинается с имени объекта (без префиксов UI или NS), это означает, что возвращаемый объект автоматически освобождается. Примеры:

[NSString stringWithFormat:@"I'm just an example: %@", text];
[UIImage imageNamed:pathToImage];

Также будьте внимательны, выпуская все объекты, которые вы создаете. Лучшее место для раскрытия свойств —
-(void)deallocметод. Локальные переменные должны быть освобождены в тот самый момент, когда они больше не нужны.

И наконец, помните, что массивы сохраняют ссылки. Следующий исходный код:

NSMutableArray *array = [NSMutableArray new];
for (NSUInteger i = 0; i < 5; i++) {
   NSString *newString = [[NSString alloc] initWithFormat: @"I'm string no %@", (i+1)];
   NSLog(@"Before adding string to array '%@', its retain count is %d", newString, [newString retainCount]);
   [array addObject: newString];
   NSLog(@"After adding string to array '%@', its retain count is %d", newString, [newString retainCount]);
}
NSString *lastInsertedString = [array lastObject];
[array release];
NSLog(@"Last object in array is '%@', its retain count is %d", [lastInsertedString retainCount]);

Results in a memory leak:

Before adding string to array 'I'm string no 1', its retain count is 1
After adding string to array 'I'm string no 1', its retain count is 2
Before adding string to array 'I'm string no 2', its retain count is 1
After adding string to array 'I'm string no 2', its retain count is 2
Before adding string to array 'I'm string no 3', its retain count is 1
After adding string to array 'I'm string no 3', its retain count is 2
Before adding string to array 'I'm string no 4', its retain count is 1
After adding string to array 'I'm string no 4', its retain count is 2
Before adding string to array 'I'm string no 5', its retain count is 1
After adding string to array 'I'm string no 5', its retain count is 2
Last object in array is 'I'm string no 5', its retain count is 1

Что вы должны сделать, это добавить
[newString release];внутри цикла, чтобы освободить вновь созданные объекты сразу после их добавления в массив. Затем, если вы повторно запустите вышеуказанный код, последняя строка приведет к плохому доступу. Зачем? потому что
lastInsertedStringобъект был наконец выпущен!

Бонусный совет: оценки

Map Kit Map Kit … При оценке чего-либо, связанного с Map Kit, умножьте его на 2 или даже на 3, если вы планируете выполнить полную настройку выносок, контактов, сгруппированных контактов, упаковки / распаковки контактов, интеграции GPS и т. Д. Map Kit — это … забудь об этом, просто придерживайся настроек по умолчанию или готовься к худшему ?

Спасибо,

Лукаш