程序员面试的那些小事程序员程序猿阵线联盟-汇总各类技术干货

iOS 面试宝典

2017-11-09  本文已影响83人  Inlight先森

tip1.可变集合类 和 不可变集合类的 copy 和 mutablecopy 有什么区别?

  1. 对于可变与不可变对象:区别在于是否需要在创建对象的时候确定并固定对象的内存地址的大小与位置。
  1. 对于深拷贝浅拷贝:区别在于是否对对象拷贝。
  1. 对于copy和mutableCopy方法:

tip2.@synthesize和@dynamic分别有什么作用?

  1. @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
  2. @synthesize 的语义是如果你没有手动实现 setter 方法和 getter 方法,那么编译器会自动为你加上这两个方法。
  3. @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

tip3.实现description方法能取到什么效果?

  1. NSLog(@"%@", objectA);这会自动调用objectA的description方法来输出ObjectA的描述信息

  2. description方法默认返回对象的描述信息(默认实现是返回类名和对象的内存地址)

  3. description方法是基类NSObject 所带的方法,因为其默认实现是返回类名和对象的内存地址, 这样的话,使用NSLog输出OC对象,意义就不是很大,因为我们并不关心对象的内存地址,比较关心的是对象内部的一些成变量的值。因此,会经常重写description方法,覆盖description方法的默认实现

  4. 重写description方法有一些坑要注意

- (NSString *)description {
    return [NSString stringWithFormat:@"%@", self];
}

tip4.为什么把NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环以后,滑动scrollview的时候NSTimer却不动了?

RunLoop只能运行在一种mode下,如果要换mode,当前的loop也需要停下重启成新的。利用这个机制,ScrollView滚动过程中NSDefaultRunLoopMode(kCFRunLoopDefaultMode)的mode会切换到UITrackingRunLoopMode来保证ScrollView的流畅滑动:只能在NSDefaultRunLoopMode模式下处理的事件会影响scrllView的滑动。

如果我们把一个NSTimer对象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主运行循环中的时候, ScrollView滚动过程中会因为mode的切换,而导致NSTimer将不再被调度。

同时因为mode还是可定制的,所以:Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes(kCFRunLoopCommonModes)来解决。

//将timer添加到NSDefaultRunLoopMode中
[NSTimer scheduledTimerWithTimeInterval:1.0
     target:self
     selector:@selector(timerTick:)
     userInfo:nil
     repeats:YES];
//然后再添加到NSRunLoopCommonModes里
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
     target:self
     selector:@selector(timerTick:)
     userInfo:nil
     repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

tip5.苹果是如何实现autoreleasepool的?

autoreleasepool以栈的数据结构实现,主要通过下列三个函数完成.

看函数名就可以知道,对autorelease分别执行push、pop操作。销毁对象时执行release操作

tip6.Block内存管理分析

  1. 根据block在内存中的位置,block被分为三种类型:

注意:在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 类型的 block。NSStackBlock会被系统自动copy一份到堆区。

  1. block什么时候在全局区,什么时候在栈上,什么时候又在堆上呢?
void(^block)(void) = ^ { NSLog(@"Global Block");};
int main() {
}
int(^block)(int count) = ^(int count) {
        return count;
    };
 block(2);

总结:配置在全局区的block,从变量作用域外也可以通过指针安全地使用

NSInteger i = 10; 
block = ^{ 
     NSLog(@"%ld", i); 
};

总结:设置在栈上的block,如果其作用域结束,该block就被销毁。

- (void)viewDidLoad {
    [super viewDidLoad];  
    NSInteger i = 10;
    self.block = { 
        NSLog(@"%ld", i);
    };
}

总结:即使变量作用域结束,堆上的Block依然存在

tip7.block里面使用self会造成循环引用吗?

很显然答案不都是,有些情况下是可以直接使用self的:

[UIView animateWithDuration:0.3 animations:^{
        [self.tableview reloadData];
    }];
void(^block)(void) = ^() {
        NSLog(@"%@", self);
    };
block();

tip8.iOS程序中的内存分配

#import "ViewController.h"

int age = 24;//全局初始化区(data区)
NSString *name;//全局未初始化区(BSS区)
static NSString *sName = @"Dely";//全局(静态初始化)区

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

int a = 10;  全局初始化区
  char *p;  全局未初始化区

 main{
    int b; 栈区
    char s[] = "abc" 栈
    char *p1; 栈 
    char *p2 = "123456";  123456\0在常量区,p2在栈上。
    NSString *number = @"abc"; //abc在堆区,number在栈上。
    NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];//分配而来的8字节的区域就在堆中,array在栈中,指向堆区的地址
    NSInteger total = [self getTotalNumber:1 number2:1];
}

- (NSInteger)getTotalNumber:(NSInteger)number1 number2:(NSInteger)number2{
    return number1 + number2;//number1和number2 栈区
}

tip9.NSCache优于NSDictionary的几点?

  1. NSCache胜过NSDictionary之处在于,当系统资源将要耗尽时,它可以自动删减缓存。如果采用普通的字典,那么就要自己编写挂钩,在系统发出“低内存”通知时手工删减缓存。

  2. NSCache并不会“拷贝”键,而是会“保留”它。此行为用NSDictionary也可以实现,然而需要编写相当复杂的代码。NSCache对象不拷贝键的原因在于:很多时候,键都是不支持拷贝操作的对象来充当的。因此,NSCache不会自动拷贝键,所以说,在键不支持拷贝操作的情况下,该类用起来比字典更方便。

  3. NSCache是线程安全的,而NSDictionary不是。

tip10.NStimer准吗?谈谈你的看法?如果不准该怎样实现一个精确的NSTimer?

不准原因:

  1. NSTimer加在main runloop中,模式是NSDefaultRunLoopMode,main负责所有主线程事件,例如UI界面的操作,复杂的运算,这样在同一个runloop中timer就会产生阻塞。

  2. 模式的改变。主线程的 RunLoop 里有两个预置的 Mode:kCFRunLoopDefaultMode(是 App 平时所处的状态) 和 UITrackingRunLoopMode(追踪 ScrollView 滑动时的状态)。当你创建一个 Timer 并加到 DefaultMode 时,Timer 会得到重复回调,但此时滑动一个 ScrollView 时,RunLoop 会将 mode 切换为 TrackingRunLoopMode,这时 Timer 就不会被回调,并且也不会影响到滑动操作。所以也会影响到 NSTimer 不准。

如何实现一个精确的 NSTimer:

  1. 在主线程中进行 NSTimer 操作,但是将 NSTimer 实例加到 main runloop 的 NSRunLoopCommonModes 中。避免被复杂运算操作或者UI界面刷新所干扰。
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(showTime) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
  1. 在子线程中进行NSTimer的操作,再在主线程中修改UI界面显示操作结果.
__block TestViewController *blockSelf = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    blockSelf->_timer=[NSTimer scheduledTimerWithTimeInterval:1.0
                                            target:blockSelf
                                        selector:@selector(caculateLeftTimeForTomorrow)
                                          userInfo:nil
                                           repeats:YES] ;
    [[NSRunLoop currentRunLoop] addTimer:blockSelf->_timer forMode:NSDefaultRunLoopMode];
    [[NSRunLoop currentRunLoop] run];
});
上一篇 下一篇

猜你喜欢

热点阅读