D20:KVC, KVO, MRC手動(dòng)內(nèi)存管理實(shí)踐

一. KVC

key value coding(鍵值編碼)

setValue:forKey:
valueForKey:

KVC是給對(duì)象屬性或成員變量賦值的一種方式
系統(tǒng)內(nèi)部采用的是元數(shù)據(jù)的方式

  1. KVC如何設(shè)置屬性或成員變量的值
  1. 如果將成員變量設(shè)置為nil值
  2. keyPath 設(shè)置屬性值
  3. KVC的獲取方法

二. KVO :以實(shí)現(xiàn)tableView的滾動(dòng)指示視圖為例

key value observe

MVC
模型數(shù)據(jù)和視圖對(duì)象之間需要通信

  1. 給視圖類加一個(gè)模型類的屬性
  2. 使用通知中心(NSNotificationCenter)
  3. 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í),

  1. 設(shè)置tableView, 手動(dòng)添加30條數(shù)據(jù)
  1. 在主視圖控制器的viewDidLoad方法中添加兩個(gè)視圖, 顯示表格滑動(dòng)的百分比, 被觀察者對(duì)象調(diào)用方法- (void)addObserver:<#(NSObject *)#> forKeyPath:<#(NSString *)#> options:<#(NSKeyValueObservingOptions)#> context:<#(void *)#>
  2. 觀察者對(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)存泄露(不影響程序的使用)

  1. 新建5個(gè)標(biāo)簽欄視圖控制器類
  1. MRC黃金法則和小試牛刀
  2. 創(chuàng)建導(dǎo)航視圖控制器, 設(shè)置完導(dǎo)航視圖控制器的根視圖控制器后將根視圖控制器對(duì)象釋放; 設(shè)置主視窗的視圖控制器 為tabBar, 然后釋放TabBar
  3. InfoViewController.m: 類方法新建成員對(duì)象后需要retain
  4. 下載數(shù)據(jù), 遵守協(xié)議, 新建表格視圖(要在dealloc方法中釋放數(shù)據(jù)源和表格視圖屬性)
  5. 新建模型類(retain屬性) , 需要在模型類的dealloc方法中釋放模型類的成員變量
  6. 處理下載數(shù)據(jù)(只寫了需要注意內(nèi)存釋放的部分), tableView的代理方法(自動(dòng)釋放池的使用)

一. KVC

  1. 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
      */  
    
  2. 如果將成員變量設(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];
            }
        }
      
  3. 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);
    
  1. KVC的獲取方法

    valueForKey:@"name"

    1. 先去找對(duì)應(yīng)的getter方法(例如@"name"會(huì)去找 -(NSString *)name; 方法)
    2. 找以下劃線開始的成員變量的值(例如: _name)
    3. 獲取同名的成員變量的值(例如name)
    4. 上面三個(gè)不符合, 調(diào)用valueForUndefinedKey:方法

二. KVO: 以實(shí)現(xiàn)tableView的滾動(dòng)指示視圖為例

  1. 設(shè)置tableView, 手動(dòng)添加30條數(shù)據(jù)
  2. 在主視圖控制器的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];
         
     }
    
  3. 觀察者對(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)存管理編寫程序

  1. 新建5個(gè)標(biāo)簽欄視圖控制器類
  2. 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;
    
  3. 創(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];
    
  4. 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];
         
     }
    
  5. 下載數(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];
        }  
      
  6. 新建模型類(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
      
  7. 處理下載數(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;
     }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.自定義控件 a.繼承某個(gè)控件 b.重寫initWithFrame方法可以設(shè)置一些它的屬性 c.在layouts...
    圍繞的城閱讀 3,694評(píng)論 2 4
  • *7月8日上午 N:Block :跟一個(gè)函數(shù)塊差不多,會(huì)對(duì)里面所有的內(nèi)容的引用計(jì)數(shù)+1,想要解決就用__block...
    炙冰閱讀 2,711評(píng)論 1 14
  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,579評(píng)論 30 472
  • 哦吼吼,又研究了幾天,把FMDB這個(gè)封裝好的數(shù)據(jù)庫(kù)搞定了,寫了個(gè)簡(jiǎn)單的例子,基于FMDB的添刪改查操作,界面很一般...
    lichengjin閱讀 662評(píng)論 0 0
  • 星兒醒了, 月兒笑了。 天際淪陷暮色。 霓虹燈光刺眼, 而你閃爍刺心。 總是想和你不期而遇, 相遇之后才知 距...
    關(guān)于我的情話閱讀 286評(píng)論 0 1

友情鏈接更多精彩內(nèi)容