学习笔记_移动开发_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > 移动开发 > 学习笔记

学习笔记

 2017/7/28 5:30:38  贝克的飞机  程序员俱乐部  我要评论(0)
  • 摘要:一、NSTimer使用时有哪些需要注意点?1、必须保证有一个活跃的RunLoop。NSTimer是基于RunLoop的一种定时机制,这涉及到默认主线程和子线程RunLoop的知识延伸;同时还有页面滑动时防止定时器失效的知识点:解决方案:[[NSRunLoopcurrentRunLoop]addTimer:timerforMode:NSRunLoopCommonModes];2、NSTimer的创建与撤销必须在同一个线程操作,不能跨越线程操作。3、存在内存泄漏的风险使用NSTimer时
  • 标签:笔记 学习 学习笔记

一、NSTimer使用时有哪些需要注意点? 

      1、必须保证有一个活跃的RunLoop。
      NSTimer是基于RunLoop的一种定时机制,这涉及到默认主线程和子线程RunLoop的知识延伸;同时还有页面滑动时防止定时器失效的知识点:
      解决方案:[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

      2、NSTimer的创建与撤销必须在同一个线程操作,不能跨越线程操作。

      3、存在内存泄漏的风险
      使用NSTimer时,timer会保持对target和userInfo参数的强引用。只有当调取了NSTimer的invalidate方法时,NSTimer才会释放target和userInfo。生成timer的方法中如果repeats参数为NO,则定时器触发后会自动调取invalidate方法。如果repeats参数为YES,则需要手动调取invalidate方法才能释放timer对target和userIfo的强引用。

    很搞笑,以前自己还写过一篇博客叫《实现定时器NSTimer的高逼格方法->GCD》讲的就是使用NSTimer需要注意的点以及使用GCD如何规避。当天面试时,可能是前面关于runtime的问题没有回答好,造成情绪波动,影响了这题的发挥。当然我也必须正视自己对知识点还没有烂熟于心,需要多温习巩固。
我的博客关于这篇文章传送门:http://www.cnblogs.com/beckwang0912/p/7027484.html

 

二、__weak如何实现对象值自动设置为nil的?

      这个问题在《Objective-C 高级编程》里面1.4的章节有_weak修饰符的详细介绍。书本也很久前看过,当天也就记起来一个内部实现是哈希表。

      在这里参考书本简单的说一下实现:

{
     id __weak obj_weak = obj;
}

     假设变量obj附加__strong修饰符且对象被赋值。
     编译器模拟代码:

id obj_weak;
obj_weak = 0;
objc_storeWeak(&obj_weak,obj);
objc_storeWeak(&obj,0);

    从上面可以很明显看出您提问的key和value分别是什么,key是赋值对象obj的内存地址value是obj_weak的内存地址。 这也可以解释您当时追问的,多个weak指针指向同一个赋值地址,怎么解决全部置nil的问题,表结构类似于下面:
?
   置nil过程如下:
   1. 从weak表中获取废弃对象的地址为键值的记录
   2. 将包含在记录中的所有附有__weak修饰符变量的地址,赋值为nil
   3. 从weak表中删除记录
   4. 从引用计数表中删除废弃对象的地址作为键值的记录

   当时还有一个追问,说为什么要置nil呢?我的回答是防止野指针。现在我按照__weak哈希表的结构推测,防止野指针应该是可行的,因为若使用__weak修饰符的变量引用对象被废弃时,不将该变量置nil,会造成该指针指向了一个不存在的地址,再次赋值或使用时可能造成crash。

 

三、Notification使用时如果在addObserver后不成对的使用removeObserver会怎么样?

      这个问题,我当时回答的是没有遇到过,确实我在工作中一直有很注意的这个问题,回来也特意检查了一下工程代码,还真有其他同事忘记移除了,但是程序也没用崩溃过。这让我很好奇,难道在因为苹果的ARC在页面pop出栈的时候自动把注册的通知移除了?不然肯定会发生向野指针对象发送消息,程序crash的情况。所有我特意写了demo尝试下,主要是思路:

      1、首先在UIViewController中注册通知,不移除。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(test) name:@"test" object:nil];

     移除注释掉:

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //[[NSNotificationCenter defaultCenter] removeObserver:self name:@"test" object:nil];
}

   2、navigationController进入到这个页面,然后pop出去。最后点击发送通知的按钮事件。

TestViewController *vc = [[TestViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];

    pop出来后点击按钮:

- (void)clickBtn:(id)sender{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"test" object:nil];
}

   我发现点击按钮后什么都没有发生,我事先准备好的日志(下图)没有打印出来,程序也没有崩溃。

- (void)test{
    NSLog(@"current thread = %@", [NSThread currentThread]);
    NSLog(@"通知调用");
}

    这更加验证了我的猜想,UIViewController自己销毁的时候悄悄的帮我暗地里移除了。有了这个想法之后,我来进一步的验证,我的验证思路是,我只要证明pop出来的时候调用了NSNotificationCenter的removeObserver方法就可以了。

#import "NSNotificationCenter+RemoveTest.h"

@implementation NSNotificationCenter (RemoveTest)

- (void)removeObserver:(id)observer{

    NSLog(@"====%@ 测试是否移除通知===", [observer class]);
}

@end

     按照上面的步骤再走一遍,神奇的事情发生了。

2017-07-27 10:33:31.610 beck.wang[1366:116266] ====__NSObserver 测试是否移除通知===
2017-07-27 10:33:31.675 beck.wang[1366:116266] ====ViewController 测试是否移除通知===
2017-07-27 10:34:52.939 beck.wang[1366:116266] ====TestViewController 测试是否移除通知===

    说明我上面的推论是正确的,UIViewController自己销毁的时候悄悄的帮我暗地里移除了。

    但是我还是推荐注册和移除成对出现,防止一些意外情况的发生,苹果也是这样推荐的。我想苹果之所以推荐注册和移除成对出现,可能是为了方式同一个通知的重复注册,不利于性能和内存,再者试想一下这样的场景,如果注册的通知没有移除,而post的时候在接受消息的对象已经释放的情况下,是否形成了给不存在对象发送消息的情况,程序是否会发生不可知的crash呢?这有待我去考证。

 

四、UITableViewCell的重用机制

      原理: UITableView只会创建一屏幕(或一屏幕多一点)的UITableViewCell,其他都是从中取出来重用的。每当Cell滑出屏幕时,就会放入到一个集合(或数组)中(这里就相当于一个重用池),当要显示某一位置的Cell时,会先去集合(或数组)中取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处可想而知,极大的减少了内存的开销。

      当时追问是这个“重用池”是怎么设计的?经过提示后,我回答的是使用字典存储,key是传入的标志符,value就是UITableViewCell。过后我查阅了资料,也只找到这样的资料:UITableView头文件中,有NSMutableArray*  visiableCells,和NSMutableDictnery* reusableTableCells两个结构。visiableCells内保存当前显示的cells,reusableTableCells保存可重用的cells。说明这个所谓的“重用池”应该就是可变字典集合[NSMutableDictnery* reusableTableCells],key为传入的标志符,value我猜测可能是个数组,用于存储UITableViewCell。

 

五、从OC调用函数叫做发送消息追问苹果为什么设计Runtime?设计它有什么好处?

      这个问题我回答的不好,回来后查阅资料,我在这里说一点自己的愚见。

      1、runtime最大的特性就是消息转发机制

      消息派发的设计使得编译期间Objective-C非常包容对象所属的类。C++里面我们可以基于称之为模板的方式实现对“类”的自定义,OC通过统一基类比如NSObject(不仅仅只有NSObject,还可以是各类根协议)对所有类新增定义。你可以向任何包括空指针nil在内的对象发你想发的消息消息派发的机制使得在不重新编译的情况下,在运行期间,干预或者说hook原来的target(方法、变量等)变得更易于实现,更有实际应用价值。

      2、runtime是OC内存管理模型的实现者

      Objective-C的内存管理原理,简单说就是“引用计数”机制。如果有模块需要引用一个对象,引用时会让对象统计用的引用计数值加1,并记录在对象的结构信息当中,当模块不再需要该对象的时候则减1,而当该对象的引用计数值为0时,就可以认为该对象不再被需要,及时销毁释放内存(回收资源)。

      3、Runtime允许我们通过标准的接口(C函数)对所有Objective-C类的变量、方法、属性以及协议等等作查询和动态扩展,从而达到我们丰富项目中语言和类库特性的目标。

      最后借鉴网上一张图说明runtime的用法:

    参考文章:

     Runtime的十种用法

     Runtime为什么是这样?

 

发表评论
用户名: 匿名