Введение в разработку под iOS (более плотное)
Вас ждет огромное количество информации. Не пытайтесь её запомнить и переварить, лучше запишите главное и запомните основные принципы.
Темы, которые мы затронем Memory management Типы данных. Сохранение настроек. Жизненный цикл приложения. Таймеры. Внешний вид приложения UIView и (почти) все его наследники Touches. Обработка касаний, распозанвание жестов Сложные интерфейсы. UINavigationController, UITabBarController. Модальные экраны. CoreGraphics. Примочки и фигульки Tips and tricks
Memory management Объект живет, если у него есть хоть один владелец У каждого объекта может быть несколько владельцев Если у объекта нет владельца, он уничтожается retainCount – фактически, количество владельцев
Как стать владельцем NSArray *a = [[NSArray alloc] init]; NSDate *d = [[NSDate date] retain]; NSString *s = [string2 copy];
Как перестать быть владельцем Немедленно: [object release]; В будущем: [object autorelease]; autorelease гарантирует сохранность объекта внутри текущей области видимости. Такое значение можно вернуть из функции, и не беспокоиться о его дальнейшей судьбе.
Например -(NSString*) formattedValue: (int) z { NSString *result = [[NSString alloc] = %05d, z]; return [result autorelease]; }
Хотя можно и так - (NSString*) formattedValue: (int) z { NSString *result = [NSString = %05d, z]; return result; }
И, да: НИКОГДА, СЛЫШИТЕ, НИКОГДА НЕЛЬЗЯ ВЫЗЫВАТЬ МЕТОД dealloc НАПРЯМУЮ Только [super dealloc];
А все-таки, когда уничтожаются autorelease-объекты? Тогда, когда уничтожается autorelease pool, к которому они принадлежат. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; …… [pool drain]; Пулы не являются вложенными, они образуют стек Поэтому создание и уничтожение пула должно происходить в одной и той же области видимости Кстати, изначально пул есть только у основного треда. В дополнительных тредах его надо создавать вручную.
Properties Вы помните, что у свойств есть модификаторы assign, retain и copy. И, да, это не просто так.
Assign -(void) setTitle: (NSString*) _title { title = _title; } Никаких хитростей, просто как бревно. Я даже раскрашивать не буду.
Retain -(void) setTitle: (NSString*) _title { if (title != _title) { [title release]; title = [_title retain]; }
Copy -(void) setTitle: (NSString*) _title { if (title != _title) { [title release]; title = [_title copy]; }
И самое главное object.title [object делают одно и то же. а между title и self.title огромная разница.
Контейнеры и память Контейнеры, как правило, всегда делают своему содержимому retain. После вызова addObject для массива, addSubview для вьюшки, и подобных вещей, можно не беспокоиться об объекте – он будет существовать до тех пор, пока существует контейнер, и будет уничтожен вместе с ним.
Еще раз: Кто сделал alloc, retain, copy – тот хозяин и обязан сделать release или autorelease Если такого не было – ты не хозяин и никому ничего не должен [object dealloc] руками не вызывать! Это самая адовая часть. Главное разобраться в ней, и уже можно считать себя айфон-разработчиком.
Типы данных Их более 9000, затронем основные NSInteger, NSUInteger, NSFloat, BOOL – просто typedefы для стандартных типов
Типы данных от CoreGraphics CGPoint – точка. (x, y) CGSize – размер (width, height) CGRect – прямоугольник (origin, size) CGPointMake(120.0, 150.0); CGSizeMake(100, 100); CGRectMake(20, 20, 160, 120);
Контейнеры NSArray NSDictionary NSSet NSMutableArray NSMutableDictionary NSMutableSet [array objectAtIndex: 5]; [dict [set anyObject]; Контейнеры могут хранить только NSObject и их потомков
Но я хочу сделать массив CGRectов! Дверь открывается, входит NSValue valueWithCGPoint: valueWithCGSize: valueWithCGRect: CGPointValue CGSizeValue CGRectValue Окей, а для int или float? [NSNumber numberWithInt: 5]; [NSNumber numberWithFloat: 5.6]; [number intValue]; [number floatValue];
Что еще? NSData NSURL NSString NSDate … И их Mutable-собратья
Ну и в заключение раздела простой способ хранить настройки NSUserDefaults *d = [NSUserDefaults standardUserDefaults]; [d setBool: YES [d setInteger: val [d setArray: stations NSString *username = [d stations = [[NSMutableArray arrayWithArray: [d retain];
Жизнь внутри приложения Runtime loop cancelButtonPressed applicationDidFinishLaunching viewWillAppear usernameValueChanged User interaction
Как заставить приложение жить независимо от действий пользователя? Способ первый: performSelector [self withObject: nil afterDelay: 0.1]; -(void) runloop { //make some actions if (currentState == kStateGameInProgress) [self withObject: nil afterDelay: 0.1]; }
Способ второй: NSTimer NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.1 target:self userInfo:nil repeats:YES]; Когда нам надоест вызываем [timer invalidate]; Как заставить приложение жить независимо от действий пользователя?
Создание интерфейса приложения Самый простой вариант один UIViewController UILabel UIButton UISegmentedControl UIImageView
IBOutlet и Colorer: UIViewController { IBOutlet UILabel* label; } -(IBAction)
IBOutlet и IBAction
Увлекательное знакомство с различными элементами интерфейса вы сможете осуществить самостоятельно, потому что про них можно рассказывать до вечера, тысячи их, хотя там все просто, как, опять же, бревно. UIView, UILabel, UIImageView, UITextField, UISlider, UIActivityIndicator, UIToolBar, UIBarButtonItem, UITextArea, UIWebView, UISwitch, UIImagePickerView, UIProgressBar, …
Самое главное, что стоит знать Все наследуется от UIView На все можно сделать addSubview: и всему можно сделать removeFromSuperview Внутри каждого UIView своя система координат. Можно строить разные цепочки вложенных UIView и делать красивые эффекты
Пожалуй, кое-на-чем все-таки стоит остановиться поподробнее [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:1.0]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; myView.center = CGPointMake(100, 100); slider.alpha = 0.5; imageView.transform = CGAffineTransformMakeRotation(-M_PI/2.0); [UIView commitAnimations]; кстати, для аффинных преобразований центром является центр UIView
Сложные интерфейсы UINavigationController DetailsViewController* dvc = [DetailsViewController alloc] [self.navigationController pushViewController: dvc animated: YES]; [dvc release]; [self.navigationController popViewControllerAnimated: YES];
UITabBarController
Другие стандартные вьюконтрллеры Существуют в изобилии. Например, для отправки смс, для отправки почты, для поиска и редактирования записей адресной книги, для навигации по фотоальбому, для съемки фото и видео, для настройки будильника и еще куча всего.
Touches - (void)touchesBegan:(NSSet *)touches withEvent: (UIEvent *) event - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event NSSet* touches = [event allTouches]; for (UITouch* t in touches) { … } Один UITouch «живет» от начала и до конца по одному и тому же адресу. Это можно и нужно учитывать при реализации взаимодействия с пользователем Ну а самое главное CGPoint p = [touch locationInView: myView];
Распознаватели жестов UIGestureRecognizer – абстрактный базовый класс для всевозможных распознавателей. Их пока что шесть: – UITapGestureRecognizer – UIPinchGestureRecognizer – UIRotationGestureRecognizer – UISwipeGestureRecognizer – UIPanGestureRecognizer – UILongPressGestureRecognizer
Как ими пользоваться: -(void) setUpRecognizers { UIRotationGestureRecognizer* r = [UIRotationGestureRecognizer alloc] initWithTarget: self [self.view addGestureRecognizer: r]; [r release]; } …. - (void) handleGesture: (UIRotationGestureRecognizer*) r { = %1.2f, vel = %1.2f, r.rotation, r.velocity); }
CoreGraphics Основной объект рисования – контекст. Доступен через CGContextRef drawRect есть только у потомков UIView, не у UIViewController - (void) drawRect:(CGRect)rect { CGContextRef myContext = UIGraphicsGetCurrentContext(); … }
CoreGraphics Второй способ – сделать самим CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); unsigned char *rawData = malloc( height * width * 4); memset( rawData,0,height * width * 4); NSUInteger bytesPerPixel = 4; NSUInteger bytesPerRow = bytesPerPixel * width; NSUInteger bitsPerComponent = 8; CGContextRef context = CGBitmapContextCreate( rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); //drawing code here // CGImageRef cgimg = CGBitmapContextCreateImage(); UIImage* result = [UIImage imageWithCGImage: cgimg];
CoreGraphics Не забываем прибраться! CGColorSpaceRelease( colorSpace ); CGContextRelease( context ); free( rawData );
А проще? Конечно, все уже сделали за вас. UIGraphicsBeginImageContext (rectSize); CGContextRef context = UIGraphicsGetCurrentContext(); //drawing code// UIImage * image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();