面试题
1 如何实现一个图片加载控件(包括图片下载、缓存时效、加载)?
参考链接:http://blog.csdn.net/tengxy_cloud/article/details/52869037
2 JavaScriptCore是做什么用的,客户端使用它可实现什么功能?
3 程序中添加每3秒响应一次的NSTimer,当拖动tableView时timer可能无法响应,为什么,要怎么解决?
scheduled开头和非schedule的开头方法的区别。系统框架提供了几种创建NSTimer的方法,其中以scheduled开头的方法会自动把timer加入当前run loop,到了设定的时间点就会触发指定的方法,而没有scheduled开头的方法则需要程序员自己手动添加到timer到一个run loop中才会有效。run loop在运行时一般有两个mode,一个defaultmode,一个trackingmode,正常情况下run loop使用defaultmode,scheduled生成的timer会默认添加到defaultmode中,当我们互动scrollview时,run loop切换到trackingmode运行,于是我们发现定时器失效了。为了使定时器在我们滑动scrollview时也能正常运行,我们需要确保defaultmode和trackingmode里都添加了我们生成的timer。如:
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(scrollPage) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
或者
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(scrollPage) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
在使用NSTimer时应注意:
(1)使用NSTimer时,timer会保持对target和userInfo参数的强引用。只有当调取了NSTimer的invalidate方法时,NSTimer才会释放target和userInfo。生成timer的方法中如果repeats参数为NO,则定时器触发后会自动调取invalidate方法。如果repeats参数为YES,则需要程序员手动调取invalidate方法才能释放timer对target和userIfo的强引用。
(2)在使用repeats参数为YES的定时器时,如果在使用完定时器时后没有调取invalidate方法,导致target和userInfo没有被释放,则可能会形成循环引用情况,从而影响内存释放。
4 客户端在解析服务端所下发的数据时,例如下面的代码片段:
NSString *sku = dict[@"sku"];
NSMutableArray *array = [NSMutableArray array];
[array addObject:sku];
可能@"sku"字段并不存在,sku对象将为nil,执行[array addObject:sku]时将会抛出异常导致Crash。项目中可能很多模块存在类似这样的问题,请从全局考虑,如何进行异常保护?
注意:在类簇中实现Method Swizzling。 NSMutableArray-----@"__NSArrayM"(用Xcode调试时,在下方debug区域显示的__NSArrayM类型)
#import "NSMutableArray+Swizzling.h" #import <objc/runtime.h> @implementation NSMutableArray (Swizzling) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class originClass = NSClassFromString(@"__NSArrayM"); Class swizzledClass = [self class]; SEL originalSelector = @selector(addObject:); SEL swizzledSelector = @selector(xxx_addObject:); Method originalMethod = class_getInstanceMethod(originClass, originalSelector); Method swizzledMethod = class_getInstanceMethod(swizzledClass, swizzledSelector); BOOL didAddMethod = class_addMethod(originClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(originClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } - (void)xxx_addObject:(id)object { if (object != nil) { [self xxx_addObject:object]; } } @end
5 当进入一个页面时添加任务到后台线程,例如下面的代码片段:
- (void)viewDidLoad {
[super viewDidLoad];
self.queue = dispatch_queue_create("jd.test", DISPATCH_QUEUE_SERIAL);
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(self.queue, ^{
NSLog(@"%ld", i);
});
}
}
在页面退出时,假如队列中执行到第3个任务,如何取消还没有执行的任务?
设置一个全局标识Bool isStart = YES,在队列中异步执行任务时,判断这个标识isStart是否为YES,如果是YES再执行,否则不执行。当页面退出时,我们在dealooc方法中将这个标识设置为isStart = NO。则队列中的任务将不再会执行,也就相当于是取消了未执行的任务。
__weak typeof(self) weakSelf = self;
dispatch_async(self.queue, ^{
if (weakSelf.isStart) {
NSLog(@"%ld", i);
}
});
6 有如下代码:
-(void)dealloc {
_weak _typeof(self) weak_self = self;
NSLog(@“%@”, weak_self);
}
请问,当执行该方法时会出现审结果?为什么?
崩溃。原因是不允许在dealloc中取weak self
崩溃时在控制台打印出的信息时:
Cannot form weak reference to instance (0x160f6f890) of class MFChatRoomBoardController. It is possible that this object was over-released, or is in the process of deallocation.
查看了一下 weak_register_no_lock
的函数代码,找到问题所在。
id weak_register_no_lock(weak_table_t *weak_table, id referent_id, id *referrer_id) { objc_object *referent = (objc_object *)referent_id; objc_object **referrer = (objc_object **)referrer_id; if (!referent || referent->isTaggedPointer()) return referent_id; // ensure that the referenced object is viable bool deallocating; if (!referent->ISA()->hasCustomRR()) { deallocating = referent->rootIsDeallocating(); } else { BOOL (*allowsWeakReference)(objc_object *, SEL) = (BOOL(*)(objc_object *, SEL)) object_getMethodImplementation((id)referent, SEL_allowsWeakReference); if ((IMP)allowsWeakReference == _objc_msgForward) { return nil; } deallocating = ! (*allowsWeakReference)(referent, SEL_allowsWeakReference); } if (deallocating) { _objc_fatal("Cannot form weak reference to instance (%p) of " "class %s. It is possible that this object was " "over-released, or is in the process of deallocation.", (void*)referent, object_getClassName((id)referent)); } // now remember it and where it is being stored weak_entry_t *entry; if ((entry = weak_entry_for_referent(weak_table, referent))) { append_referrer(entry, referrer); } else { weak_entry_t new_entry; new_entry.referent = referent; new_entry.out_of_line = 0; new_entry.inline_referrers[0] = referrer; for (size_t i = 1; i < WEAK_INLINE_COUNT; i++) { new_entry.inline_referrers[i] = nil; } weak_grow_maybe(weak_table); weak_entry_insert(weak_table, &new_entry); } // Do not set *referrer. objc_storeWeak() requires that the // value not change. return referent_id; }
可以看出,runtime 是通过检查引用计数的个数来判断对象是否在 deallocting, 然后通iif (deallocating)使程序崩溃。
7 有如下代码:
typedef void (^TestBlock)(void);
int main(int argc, char * argv[]) {
@autoreleasepool {
NSString *test = @"test";
TestBlock block = ^(void) {
dispatch_sync(dispatch_queue_create("jd.test", DISPATCH_QUEUE_SERIAL), ^{
NSLog(@"%@", test);
});
};
test = @"test1";
block();
}
}
请问,NSLog(@“%@”, test)的输出结果是什么?这条语句在哪个线程执行?为什么?
输出结果:test 在主线程执行 同步执行