一. KVC
key value coding(鍵值編碼)
setValue:forKey:
valueForKey:
KVC是給對(duì)象屬性或成員變量賦值的一種方式
系統(tǒng)內(nèi)部采用的是元數(shù)據(jù)的方式
- KVC如何設(shè)置屬性或成員變量的值
- 如果將成員變量設(shè)置為nil值
- keyPath 設(shè)置屬性值
- KVC的獲取方法
二. KVO :以實(shí)現(xiàn)tableView的滾動(dòng)指示視圖為例
key value observe
MVC
模型數(shù)據(jù)和視圖對(duì)象之間需要通信
- 給視圖類加一個(gè)模型類的屬性
- 使用通知中心(NSNotificationCenter)
- KVO
KVO: 一個(gè)對(duì)象去監(jiān)聽另外一個(gè)對(duì)象的某一個(gè)屬性, 當(dāng)該屬性變化時(shí), 做相應(yīng)的處理
ColorView對(duì)象去監(jiān)聽ColorModel對(duì)象的color屬性, 當(dāng)color屬性變化時(shí),
- 設(shè)置tableView, 手動(dòng)添加30條數(shù)據(jù)
- 在主視圖控制器的viewDidLoad方法中添加兩個(gè)視圖, 顯示表格滑動(dòng)的百分比, 被觀察者對(duì)象調(diào)用方法- (void)addObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(void *)#>
- 觀察者對(duì)象所屬類實(shí)現(xiàn) - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
三. 利用MRC手動(dòng)內(nèi)存管理編寫程序
黃金法則:
- alloc copy new mutableCopy創(chuàng)建對(duì)象, 自動(dòng)引用計(jì)數(shù)器為1
- 調(diào)用retain時(shí)會(huì)將引用計(jì)數(shù)加1, release會(huì)將引用計(jì)數(shù)減1
- 在什么地方增加了引用計(jì)數(shù), 就要在什么地方減少引用計(jì)數(shù)
釋放次數(shù)多了, 程序會(huì)崩潰
忘記釋放, 內(nèi)存泄露(不影響程序的使用)
- 新建5個(gè)標(biāo)簽欄視圖控制器類
- MRC黃金法則和小試牛刀
- 創(chuàng)建導(dǎo)航視圖控制器, 設(shè)置完導(dǎo)航視圖控制器的根視圖控制器后將根視圖控制器對(duì)象釋放; 設(shè)置主視窗的視圖控制器 為tabBar, 然后釋放TabBar
- InfoViewController.m: 類方法新建成員對(duì)象后需要retain
- 下載數(shù)據(jù), 遵守協(xié)議, 新建表格視圖(要在dealloc方法中釋放數(shù)據(jù)源和表格視圖屬性)
- 新建模型類(retain屬性) , 需要在模型類的dealloc方法中釋放模型類的成員變量
- 處理下載數(shù)據(jù)(只寫了需要注意內(nèi)存釋放的部分), tableView的代理方法(自動(dòng)釋放池的使用)
一. KVC
-
KVC如何設(shè)置屬性或成員變量的值
User *user1 = [[User alloc] init]; user1.name = @"余文樂(lè)"; NSLog(@"%@", user1.name); // KVC // 一. KVC如何設(shè)置屬性或成員變量的值 // 1. 使用KVC的方式賦值默認(rèn)首先調(diào)用對(duì)應(yīng)的set方法 // 默認(rèn)去找setName:方法, 如果找到就調(diào)用 [user1 setValue:@"吳彥祖" forKey:@"name"]; NSLog(@"name:%@", user1.name); // 2. 找不到set方法, 會(huì)去找同名的以下劃線開頭的成員變量 [user1 setValue:@"China" forKey:@"country"]; NSLog(@"Country:%@", user1->_country); // 3. 找不到前兩者的情況下, 找同名的成員變量 [user1 setValue:@"HongKong" forKey:@"city"]; [user1 showCity]; // 4. 上面三種情況都不符合 // 會(huì)調(diào)用setValue:forUndefinedKey:方法 // 這個(gè)方法的默認(rèn)實(shí)現(xiàn)是拋出一個(gè)異常 [user1 setValue:@"尖沙咀" forKey:@"address"]; /* Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<User 0x7fce21733a20> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key address.' */ /* 是將字典里面的key值遍歷, 調(diào)用了setValue:forKey:方法 - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues */ -
如果將成員變量設(shè)置為nil值
-
成員變量
// 性別 NSString *_gender; // 年齡 int age; -
設(shè)置成員變量
[user1 setValue:nil forKey:@"gender"]; // 設(shè)置基本類型的值, 不會(huì)報(bào)錯(cuò) // @50 == [NSNumber numberWithInt:50]; [user1 setValue:@50 forKey:@"age"]; [user1 setValue:nil forKey:@"age"]; /* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[<User 0x7fa188617e00> setNilValueForKey]: could not set nil as the value for the key age.' */ -
將基本類型設(shè)為nil時(shí)報(bào)錯(cuò)的解決辦法
// 設(shè)置nil值的時(shí)候調(diào)用這個(gè)方法 - (void)setNilValueForKey:(NSString *)key { if ([key isEqualToString:@"age"]) { age = 0; } else { [super setNilValueForKey:key]; } }
-
-
keyPath 設(shè)置屬性值
-
house.h
#import <Foundation/Foundation.h> @interface House : NSObject @property (nonatomic, assign) float price; // 百萬(wàn)元為單位 @end
-
-
ViewController.m
House *h = [[House alloc] init];
user1.house = h;// 設(shè)置房子的價(jià)格 [user1 setValue:@3 forKeyPath:@"house.price"]; NSLog(@"%F", user1.house.price);
-
KVC的獲取方法
valueForKey:@"name"
- 先去找對(duì)應(yīng)的getter方法(例如@"name"會(huì)去找 -(NSString *)name; 方法)
- 找以下劃線開始的成員變量的值(例如: _name)
- 獲取同名的成員變量的值(例如name)
- 上面三個(gè)不符合, 調(diào)用valueForUndefinedKey:方法
二. KVO: 以實(shí)現(xiàn)tableView的滾動(dòng)指示視圖為例
-
設(shè)置tableView, 手動(dòng)添加30條數(shù)據(jù)
-
在主視圖控制器的viewDidLoad方法中添加兩個(gè)視圖, 顯示表格滑動(dòng)的百分比, 被觀察者對(duì)象調(diào)用方法
- (void)addObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(void *)#>- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self createTableView]; [self creatDataArray]; // 添加兩個(gè)視圖, 顯示表格滑動(dòng)的百分比 UIView *grayView = [[UIView alloc] initWithFrame:CGRectMake(330, 60, 20, 500)]; grayView.backgroundColor = [UIColor grayColor]; [self.view addSubview:grayView]; UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(330, 60, 20, 0)]; redView.backgroundColor = [UIColor redColor]; redView.tag = 200; [self.view addSubview:redView]; // KVO實(shí)現(xiàn) // contentOffset // self對(duì)象監(jiān)聽_tableView的contentOffset屬性的變化 // 被觀察者對(duì)象調(diào)用這個(gè)方法 [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; } -
觀察者對(duì)象所屬類實(shí)現(xiàn)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // 屬性 if ([keyPath isEqualToString:@"contentOffset"]) { // 被觀察者對(duì)象 if ([object isKindOfClass:[UITableView class]]) { // NSLog(@"%s", __func__); NSValue *value = change[@"new"]; // NSValue類型的值: change[@"new"] CGPoint point = [value CGPointValue]; // 計(jì)算當(dāng)前的高度 float ratio = point.y/(self.tableView.contentSize.height - self.tableView.bounds.size.height); CGFloat height = ratio * 500; // 設(shè)置redView的高度 UIView *redView = [self.view viewWithTag:200]; if (height >= 0) { CGRect frame = redView.frame; frame.size.height = height; redView.frame = frame; } } } }
三. 利用MRC手動(dòng)內(nèi)存管理編寫程序
-
新建5個(gè)標(biāo)簽欄視圖控制器類
-
MRC黃金法則和小試牛刀
// 黃金法則: // 1. alloc copy new mutableCopy創(chuàng)建對(duì)象, 自動(dòng)引用計(jì)數(shù)器為1 // 2. 調(diào)用retain時(shí)會(huì)將引用計(jì)數(shù)加1, release會(huì)將引用計(jì)數(shù)減1 // 3. 在什么地方增加了引用計(jì)數(shù), 就要在什么地方減少引用計(jì)數(shù) // 綜合 InfoViewController *infoCtrl = [[InfoViewController alloc] init]; infoCtrl.tabBarItem.title = @"綜合"; infoCtrl.tabBarItem.image = [UIImage imageNamed:@"info.png"]; // .png默認(rèn)可以省略 UINavigationController *infoNavCtrl = [[UINavigationController alloc] initWithRootViewController:infoCtrl]; // infoCtrl 對(duì)象不再需要, 釋放這個(gè)對(duì)象 [infoCtrl release]; return YES; -
創(chuàng)建導(dǎo)航視圖控制器, 設(shè)置完導(dǎo)航視圖控制器的根視圖控制器后將根視圖控制器對(duì)象釋放; 設(shè)置主視窗的視圖控制器為tabBar, 然后釋放TabBar
// 問(wèn)答 AnswerViewController *answerCtrl = [[AnswerViewController alloc] init]; answerCtrl.tabBarItem.title = @"問(wèn)答"; answerCtrl.tabBarItem.image = [UIImage imageNamed:@"answer"]; UINavigationController *answerNavCtrl = [[UINavigationController alloc] initWithRootViewController:answerCtrl]; // 釋放answerCtrl對(duì)象 [answerCtrl release]; // 動(dòng)彈 TweetViewController *tweetCtrl = [[TweetViewController alloc] init]; tweetCtrl.tabBarItem.title = @"動(dòng)彈"; tweetCtrl.tabBarItem.image = [UIImage imageNamed:@"tweet"]; // .png默認(rèn)可以省略 UINavigationController *tweetNavCtrl = [[UINavigationController alloc] initWithRootViewController:tweetCtrl]; // tweetCtrl 對(duì)象不再需要, 釋放這個(gè)對(duì)象 [tweetCtrl release]; // 我的 MineViewController *mineCtrl = [[MineViewController alloc] init]; mineCtrl.tabBarItem.title = @"我的"; mineCtrl.tabBarItem.image = [UIImage imageNamed:@"active"]; UINavigationController *mineNavCtrl = [[UINavigationController alloc] initWithRootViewController:mineCtrl]; // 釋放mineCtrl對(duì)象 [mineCtrl release]; // 更多 MoreViewController *moreCtrl = [[MoreViewController alloc] init]; moreCtrl.tabBarItem.title = @"更多"; moreCtrl.tabBarItem.image = [UIImage imageNamed:@"more"]; UINavigationController *moreNavCtrl = [[UINavigationController alloc] initWithRootViewController:moreCtrl]; // 釋放moreCtrl對(duì)象 [moreCtrl release]; // 創(chuàng)建tabBar對(duì)象 UITabBarController *tabBarCtrl = [[UITabBarController alloc] init]; tabBarCtrl.viewControllers = @[infoNavCtrl, answerNavCtrl, tweetNavCtrl, mineNavCtrl, moreNavCtrl]; // 所有導(dǎo)航視圖控制器對(duì)象不再需要, 釋放內(nèi)存 [infoNavCtrl release]; [answerNavCtrl release]; [tweetNavCtrl release]; [mineNavCtrl release]; [moreNavCtrl release]; // 主窗口的視圖控制器 self.window.rootViewController = tabBarCtrl; [tabBarCtrl release]; -
InfoViewController.m: 類方法新建成員對(duì)象后需要retain
#import "InfoViewController.h" @interface InfoViewController () { UITableView *_tbView; NSMutableArray *_dataArray; } @end @implementation InfoViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // _dataArray = [[NSMutableArray alloc] init]; _dataArray = [NSMutableArray array]; [_dataArray retain]; } -
下載數(shù)據(jù), 遵守協(xié)議, 新建表格視圖(要在dealloc方法中釋放數(shù)據(jù)源和表格視圖屬性)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // _dataArray = [[NSMutableArray alloc] init]; _dataArray = [NSMutableArray array]; [_dataArray retain]; // 導(dǎo)航 self.automaticallyAdjustsScrollViewInsets = NO; // 表格 _tbView = [[UITableView alloc] initWithFrame:CGRectMake(0, 64, 375, 667 - 64 - 49) style:UITableViewStylePlain]; _tbView.delegate = self; _tbView.dataSource = self; [self.view addSubview:_tbView]; // 下載數(shù)據(jù) _receivedData = [[NSMutableData alloc] init]; [self downloadData]; } - (void)downloadData { // http://www.oschina.net/action/api/tweet_list?uid=0&pageIndex=0&pageSize=20 // 不需要考慮內(nèi)存問(wèn)題 [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.oschina.net/action/api/tweet_list?uid=0&pageIndex=0&pageSize=20"]] delegate:self]; }-
在dealloc中釋放對(duì)象, dealloc方法不一定立即執(zhí)行
- (void)dealloc
{
[_dataArray release];
_tbView.delegate = nil;
_tbView.dataSource = nil;
[_tbView release];[_receivedData release]; // 父類的方法 // MRC下一定要調(diào)用父類的dealloc, ARC下不能調(diào)用 [super dealloc]; }
-
-
新建模型類(retain屬性) , 需要在模型類的dealloc方法中釋放模型類的成員變量
-
TweetModel.h
#import <Foundation/Foundation.h>@interface TweetModel : NSObject // 使用retain @property (nonatomic, retain) NSString *author; @property (nonatomic, retain) NSString *body; @end -
TweetModel.m
#import "TweetModel.h"@implementation TweetModel - (void)dealloc { [_author release]; [_body release]; [super dealloc]; } //- (void)setAuthor:(NSString *)author //{ // if (_author != author) { // [_author release]; // _author = [author retain]; // } //} @end
-
-
處理下載數(shù)據(jù)(只寫了需要注意內(nèi)存釋放的部分), tableView的代理方法(自動(dòng)釋放池的使用)
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:_receivedData options:0 error:nil]; // "http://" 獲取任意位置的tweet節(jié)點(diǎn) NSArray *tweetNodes = [doc nodesForXPath:@"http://tweet" error:nil]; for (GDataXMLElement *elements in tweetNodes) { // 創(chuàng)建模型對(duì)象 TweetModel *model = [[TweetModel alloc] init]; model.author = [[[elements elementsForName:@"author"] lastObject] stringValue]; model.body = [[[elements elementsForName:@"body"] lastObject] stringValue]; [_dataArray addObject:model]; // 釋放model對(duì)象 [model release]; } // 刷新表格 [_tbView reloadData]; // 釋放doc兌現(xiàn) [doc release]; } #pragma mark - UITableView代理方法 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return _dataArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellId = @"cellID"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; if (nil == cell) { #warning 自動(dòng)釋放池 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId] autorelease]; } // 模型對(duì)象 TweetModel *model = _dataArray[indexPath.row]; cell.textLabel.text = model.author; cell.detailTextLabel.text = model.body; return cell; }