iOS之规范3

iOS之规范3

参考链接:参考1参考2参考3

能否向编译后得到的类中增加实例变量?能否向运行时创建的类中添加实例变量?为什么?

答:

  • 不能向编译后得到的类中增加实例变量

    因为编译后的类已经注册在 runtime 中,类结构体中的 objc_ivar_list 实例变量的链表 和 instance_size 实例变量的内存大小已经确定,同时 runtime 会调用 class_setIvarLayoutclass_setWeakIvarLayout 来处理 strong weak 引用。所以不能向存在的类中添加实例变量;

  • 能向运行时创建的类中添加实例变量

    运行时创建的类是可以添加实例变量,调用 class_addIvar 函数。但是得在调用 objc_allocateClassPair 之后,objc_registerClassPair 之前,原因同上。

runloop和线程有什么关系?

  • runloop:一直在运行着的循环.

  • 关系:

    run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop (以下都以 Cocoa 为例)

    • 主线程的run loop默认是启动的.

      APPDelegate中的UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象。
      * 对其它线程来说,run loop默认是没有启动的。

      如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
      * 在任何一个 Cocoa 程序的线程中,都可以通过以下代码来获取到当前线程的 run loop:

      NSRunLoop *runloop = [NSRunLoop currentRunLoop];

参考链接:《Objective-C之run loop详解》

runloop的 mode

mode 作用:

答案:主要用来指定事件在运行循环(runloop)中的优先级的。

  • NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默认,空闲状态------<mark>apple 公开提供
  • UITrackingRunLoopMode:ScrollView滑动时
  • UIInitializationRunLoopMode:启动时
  • NSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合------<mark>apple 公开提供

以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

答案:

  • 原因:

    RunLoop只能运行在一种mode下,如果要换mode,当前的runloop也需要停下重启成新的。

    + scheduledTimerWithTimeInterval是将 timer 以 defaultmode 添加至当前 runloop(主线程)中。

    利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动(相互影响)。

  • 解决方案:

    将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。(即在任何 mode 下都执行 timer 的计时操作)

    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
     target:self
     selector:@selector(timerTick:)
     userInfo:nil
     repeats:YES];
    

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
```

objc 使用什么机制管理对象内存?

答案:

  • 通过 retaincount 机制来决定对象是否需要释放。
  • 每次 runloop 时,都会检测是否有 retaincount 为0的对象,若有,则释放之。

ARC 通过什么方式帮助管理内存?

答案:

  • ARC 之于 MRC,不是简单的在编译时添加retain、release、autorelease,而是在编译时运行时2个部分共同管理内存。
  • 编译时:ARC 使用底层 c 接口实现retain、release、autorelease,此举使之性能更好。成对优化。
  • 运行时:

一个 autorelease 对象在什么时刻释放?

(如在一个 ctrl 的 viewDidLoad 中创建)

答:

分为2种情况:

  • 手动干预释放时机

    指定autoreleasepool 就是所谓的:当前作用域大括号结束时释放.

  • 系统自动释放

    不手动指定autoreleasepool.

    Autorelease对象是在当前的runloop迭代结束时释放的。

    而它能够释放的原因是系统在每个runloop迭代中都加入了自动释放池Push和Pop.继而Autorelease对象出了作用域之后,会被添加到最近一次创建的自动释放池中,并会在当前的 runloop 迭代结束时释放.

    @autoreleasepool 当自动释放池被销毁或者耗尽时,会向自动释放池中的所有对象发送 release 消息,释放自动释放池中的所有对象。

苹果是如何实现autoreleasepool的?

答案:
autoreleasepool 以一个队列数组形式实现,主要以三个函数完成:

  • objc_autoreleasepoolPush
  • objc_autoreleasepoolPop
  • objc_autorelease

BAD_ACCESS在什么情况下出现?

答:访问野指针。如:对一个已经释放的对象发消息、访问该对象的成员,死循环等。

使用block时什么情况会发生引用循环,如何解决?

答:

对象中强引用了 block,在 block 中又强引用了该对象。即会发生引用循环。

解决方案:

将对象用__block__weak修饰之后,再在 block 中使用。

使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?

答:

所谓引用循环是指双向的强引用,所以那些单向的强引用(block 只强引用 self )没有问题。

  • 单项引用不考虑的情况:

[UIView animateWithDuration:duration animations:^{ [self.superview layoutIfNeeded]; }];

[[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.someProperty = xyz; }];

[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification) {
self.someProperty = xyz; }];


* 双项引用考虑的情况:

    > 使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用.
    
    ```
    __weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^
{
__typeof__(self) strongSelf = weakSelf;
[strongSelf doSomething];
[strongSelf doSomethingElse];
} );
    ```
    ```
     __weak __typeof__(self) weakSelf = self;
  _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
                                                                object:nil
                                                                 queue:nil
                                                            usingBlock:^(NSNotification *note) {
      __typeof__(self) strongSelf = weakSelf;
      [strongSelf dismissModalViewControllerAnimated:YES];
  }];
  //self --> _observer --> block --> self 显然这也是一个循环引用。
    ```
    
## dispatch_barrier_async的作用是什么?

答:

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。

> 在并行队列中,为了保持某些任务的顺序,需要等待一些任务完成后才能继续进行,使用 barrier 来等待之前任务完成,避免数据竞争等问题。 dispatch_barrier_async 函数会等待追加到Concurrent Dispatch Queue并行队列中的操作全部执行完之后,然后再执行 dispatch_barrier_async 函数追加的处理,等 dispatch_barrier_async 追加的处理执行结束之后,Concurrent Dispatch Queue才恢复之前的动作继续执行。

> 注意:使用 dispatch_barrier_async ,该函数只能搭配自定义并行队列 dispatch_queue_t 使用。不能使用: dispatch_get_global_queue ,否则 dispatch_barrier_async 的作用会和 dispatch_async 的作用一模一样。 )

dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"dispatch_async1");
});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:4];
NSLog(@"dispatch_async2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"dispatch_barrier_async");
[NSThread sleepForTimeInterval:4];

});
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"dispatch_async3");
});


结果:

2012-09-25 16:20:33.967 gcdTest[45547:11203] dispatch_async1
2012-09-25 16:20:35.967 gcdTest[45547:11303] dispatch_async2
2012-09-25 16:20:35.967 gcdTest[45547:11303] dispatch_barrier_async
2012-09-25 16:20:40.970 gcdTest[45547:11303] dispatch_async3


## 如何手动出发一个 value 的 KVO?

答案:

* 触发 KVO 原理:

    > 键值观察通知依赖于 NSObject 的两个方法: `willChangeValueForKey:` 和 `didChangevlueForKey:` 。
    > 
    > 在一个被观察属性发生改变之前, `willChangeValueForKey:` 一定会被调用,这就 会记录旧的值。而当改变发生后, didChangeValueForKey: 会被调用,继而 observeValueForKey:ofObject:change:context: 也会被调用。
    > 
    > 如果可以手动实现这些调用,就可以实现“手动触发”了。
    
* 手动出发目的:希望能控制`回调的调用时机`

    ```
    - (void)viewDidLoad
{
    [super viewDidLoad];
    [self willChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。
    [self didChangeValueForKey:@"now"]; // “手动触发self.now的KVO”,必写。
}

    ```
    
* 不建议`手动触发`
* KVO 的 keyPath不仅支持属性,也支持实例变量。

## apple 实现 KVO 的方式

KVO 的实现依赖于 isa-swizzling 技术。

*  当对象的一个属性被注册监听后,对象的 isa 指针转而指向一个中间类,而非原来的类对象了。
 
* 中间类对象:继承自原来的类对象。但重写了被观察的属性 setter 方法。

* 重写 setter 方法:在原来 setter 方法之前之后(willChangeValueForKey、didChangevlueForKey),通知所有观察对象:值的改变。
* 继而会调用observeValueForKey:ofObject:change:context:。


## EXC_BAD_ACCESS之BUG解决

1. 重写 objc 的 respondsToselector 方法,显示出现`EXEC_BAD_ACCESS`前访问的最后一个object:

    ```
     #ifdef _FOR_DEBUG_  
-(BOOL) respondsToSelector:(SEL)aSelector {  
    printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);  
    return [super respondsToSelector:aSelector];  
}  
#endif  
    ```
    
2. 通过设置 [NSZombieEnable](http://mobile.51cto.com/iphone-279455.htm)
3. 设置全局断点快速定位问题代码所在行
4. xcode7中有 address Sanitizer
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容