一. KVC
key value coding(键值编码)
setValue:forKey:
valueForKey:
KVC是给对象属性或成员变量赋值的一种方式
系统内部采用的是元数据的方式
- KVC如何设置属性或成员变量的值
- 如果将成员变量设置为nil值
- keyPath 设置属性值
- KVC的获取方法
二. KVO :以实现tableView的滚动指示视图为例
key value observe
MVC
模型数据和视图对象之间需要通信
- 给视图类加一个模型类的属性
- 使用通知中心(NSNotificationCenter)
- KVO
KVO: 一个对象去监听另外一个对象的某一个属性, 当该属性变化时, 做相应的处理
ColorView对象去监听ColorModel对象的color属性, 当color属性变化时,
- 设置tableView, 手动添加30条数据
- 在主视图控制器的viewDidLoad方法中添加两个视图, 显示表格滑动的百分比, 被观察者对象调用方法- (void)addObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(void *)#>
- 观察者对象所属类实现 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
三. 利用MRC手动内存管理编写程序
黄金法则:
- alloc copy new mutableCopy创建对象, 自动引用计数器为1
- 调用retain时会将引用计数加1, release会将引用计数减1
- 在什么地方增加了引用计数, 就要在什么地方减少引用计数
释放次数多了, 程序会崩溃
忘记释放, 内存泄露(不影响程序的使用)
- 新建5个标签栏视图控制器类
- MRC黄金法则和小试牛刀
- 创建导航视图控制器, 设置完导航视图控制器的根视图控制器后将根视图控制器对象释放; 设置主视窗的视图控制器 为tabBar, 然后释放TabBar
- InfoViewController.m: 类方法新建成员对象后需要retain
- 下载数据, 遵守协议, 新建表格视图(要在dealloc方法中释放数据源和表格视图属性)
- 新建模型类(retain属性) , 需要在模型类的dealloc方法中释放模型类的成员变量
- 处理下载数据(只写了需要注意内存释放的部分), tableView的代理方法(自动释放池的使用)
一. KVC
-
KVC如何设置属性或成员变量的值
User *user1 = [[User alloc] init]; user1.name = @"余文乐"; NSLog(@"%@", user1.name); // KVC // 一. KVC如何设置属性或成员变量的值 // 1. 使用KVC的方式赋值默认首先调用对应的set方法 // 默认去找setName:方法, 如果找到就调用 [user1 setValue:@"吴彦祖" forKey:@"name"]; NSLog(@"name:%@", user1.name); // 2. 找不到set方法, 会去找同名的以下划线开头的成员变量 [user1 setValue:@"China" forKey:@"country"]; NSLog(@"Country:%@", user1->_country); // 3. 找不到前两者的情况下, 找同名的成员变量 [user1 setValue:@"HongKong" forKey:@"city"]; [user1 showCity]; // 4. 上面三种情况都不符合 // 会调用setValue:forUndefinedKey:方法 // 这个方法的默认实现是抛出一个异常 [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值遍历, 调用了setValue:forKey:方法 - (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues */
-
如果将成员变量设置为nil值
-
成员变量
// 性别 NSString *_gender; // 年龄 int age;
-
设置成员变量
[user1 setValue:nil forKey:@"gender"]; // 设置基本类型的值, 不会报错 // @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.' */
-
将基本类型设为nil时报错的解决办法
// 设置nil值的时候调用这个方法 - (void)setNilValueForKey:(NSString *)key { if ([key isEqualToString:@"age"]) { age = 0; } else { [super setNilValueForKey:key]; } }
-
-
keyPath 设置属性值
-
house.h
#import <Foundation/Foundation.h> @interface House : NSObject @property (nonatomic, assign) float price; // 百万元为单位 @end
-
-
ViewController.m
House *h = [[House alloc] init];
user1.house = h;// 设置房子的价格 [user1 setValue:@3 forKeyPath:@"house.price"]; NSLog(@"%F", user1.house.price);
-
KVC的获取方法
valueForKey:@"name"
- 先去找对应的getter方法(例如@"name"会去找 -(NSString *)name; 方法)
- 找以下划线开始的成员变量的值(例如: _name)
- 获取同名的成员变量的值(例如name)
- 上面三个不符合, 调用valueForUndefinedKey:方法
二. KVO: 以实现tableView的滚动指示视图为例
-
设置tableView, 手动添加30条数据
-
在主视图控制器的viewDidLoad方法中添加两个视图, 显示表格滑动的百分比, 被观察者对象调用方法
- (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]; // 添加两个视图, 显示表格滑动的百分比 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实现 // contentOffset // self对象监听_tableView的contentOffset属性的变化 // 被观察者对象调用这个方法 [self.tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil]; }
-
观察者对象所属类实现
- (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"]) { // 被观察者对象 if ([object isKindOfClass:[UITableView class]]) { // NSLog(@"%s", __func__); NSValue *value = change[@"new"]; // NSValue类型的值: change[@"new"] CGPoint point = [value CGPointValue]; // 计算当前的高度 float ratio = point.y/(self.tableView.contentSize.height - self.tableView.bounds.size.height); CGFloat height = ratio * 500; // 设置redView的高度 UIView *redView = [self.view viewWithTag:200]; if (height >= 0) { CGRect frame = redView.frame; frame.size.height = height; redView.frame = frame; } } } }
三. 利用MRC手动内存管理编写程序
-
新建5个标签栏视图控制器类
-
MRC黄金法则和小试牛刀
// 黄金法则: // 1. alloc copy new mutableCopy创建对象, 自动引用计数器为1 // 2. 调用retain时会将引用计数加1, release会将引用计数减1 // 3. 在什么地方增加了引用计数, 就要在什么地方减少引用计数 // 综合 InfoViewController *infoCtrl = [[InfoViewController alloc] init]; infoCtrl.tabBarItem.title = @"综合"; infoCtrl.tabBarItem.image = [UIImage imageNamed:@"info.png"]; // .png默认可以省略 UINavigationController *infoNavCtrl = [[UINavigationController alloc] initWithRootViewController:infoCtrl]; // infoCtrl 对象不再需要, 释放这个对象 [infoCtrl release]; return YES;
-
创建导航视图控制器, 设置完导航视图控制器的根视图控制器后将根视图控制器对象释放; 设置主视窗的视图控制器为tabBar, 然后释放TabBar
// 问答 AnswerViewController *answerCtrl = [[AnswerViewController alloc] init]; answerCtrl.tabBarItem.title = @"问答"; answerCtrl.tabBarItem.image = [UIImage imageNamed:@"answer"]; UINavigationController *answerNavCtrl = [[UINavigationController alloc] initWithRootViewController:answerCtrl]; // 释放answerCtrl对象 [answerCtrl release]; // 动弹 TweetViewController *tweetCtrl = [[TweetViewController alloc] init]; tweetCtrl.tabBarItem.title = @"动弹"; tweetCtrl.tabBarItem.image = [UIImage imageNamed:@"tweet"]; // .png默认可以省略 UINavigationController *tweetNavCtrl = [[UINavigationController alloc] initWithRootViewController:tweetCtrl]; // tweetCtrl 对象不再需要, 释放这个对象 [tweetCtrl release]; // 我的 MineViewController *mineCtrl = [[MineViewController alloc] init]; mineCtrl.tabBarItem.title = @"我的"; mineCtrl.tabBarItem.image = [UIImage imageNamed:@"active"]; UINavigationController *mineNavCtrl = [[UINavigationController alloc] initWithRootViewController:mineCtrl]; // 释放mineCtrl对象 [mineCtrl release]; // 更多 MoreViewController *moreCtrl = [[MoreViewController alloc] init]; moreCtrl.tabBarItem.title = @"更多"; moreCtrl.tabBarItem.image = [UIImage imageNamed:@"more"]; UINavigationController *moreNavCtrl = [[UINavigationController alloc] initWithRootViewController:moreCtrl]; // 释放moreCtrl对象 [moreCtrl release]; // 创建tabBar对象 UITabBarController *tabBarCtrl = [[UITabBarController alloc] init]; tabBarCtrl.viewControllers = @[infoNavCtrl, answerNavCtrl, tweetNavCtrl, mineNavCtrl, moreNavCtrl]; // 所有导航视图控制器对象不再需要, 释放内存 [infoNavCtrl release]; [answerNavCtrl release]; [tweetNavCtrl release]; [mineNavCtrl release]; [moreNavCtrl release]; // 主窗口的视图控制器 self.window.rootViewController = tabBarCtrl; [tabBarCtrl release];
-
InfoViewController.m: 类方法新建成员对象后需要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]; }
-
下载数据, 遵守协议, 新建表格视图(要在dealloc方法中释放数据源和表格视图属性)
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // _dataArray = [[NSMutableArray alloc] init]; _dataArray = [NSMutableArray array]; [_dataArray retain]; // 导航 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]; // 下载数据 _receivedData = [[NSMutableData alloc] init]; [self downloadData]; } - (void)downloadData { // http://www.oschina.net/action/api/tweet_list?uid=0&pageIndex=0&pageSize=20 // 不需要考虑内存问题 [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.oschina.net/action/api/tweet_list?uid=0&pageIndex=0&pageSize=20"]] delegate:self]; }
-
在dealloc中释放对象, dealloc方法不一定立即执行
- (void)dealloc
{
[_dataArray release];
_tbView.delegate = nil;
_tbView.dataSource = nil;
[_tbView release];[_receivedData release]; // 父类的方法 // MRC下一定要调用父类的dealloc, ARC下不能调用 [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
-
-
处理下载数据(只写了需要注意内存释放的部分), tableView的代理方法(自动释放池的使用)
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:_receivedData options:0 error:nil]; // "//" 获取任意位置的tweet节点 NSArray *tweetNodes = [doc nodesForXPath:@"//tweet" error:nil]; for (GDataXMLElement *elements in tweetNodes) { // 创建模型对象 TweetModel *model = [[TweetModel alloc] init]; model.author = [[[elements elementsForName:@"author"] lastObject] stringValue]; model.body = [[[elements elementsForName:@"body"] lastObject] stringValue]; [_dataArray addObject:model]; // 释放model对象 [model release]; } // 刷新表格 [_tbView reloadData]; // 释放doc兑现 [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 自动释放池 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId] autorelease]; } // 模型对象 TweetModel *model = _dataArray[indexPath.row]; cell.textLabel.text = model.author; cell.detailTextLabel.text = model.body; return cell; }