Fork me on GitHub

面试题

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。如:

1
2
3
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];

 或者

1
2
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 客户端在解析服务端所下发的数据时,例如下面的代码片段:

1
2
3
4
5
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类型)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#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 当进入一个页面时添加任务到后台线程,例如下面的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (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。则队列中的任务将不再会执行,也就相当于是取消了未执行的任务。

1
2
3
4
5
6
7
8
9
10
11
__weak typeof(self) weakSelf = self;  
 
dispatch_async(self.queue, ^{
 
  if (weakSelf.isStart) {
 
    NSLog(@"%ld", i);
 
  }    
 
      });

 

6 有如下代码:

1
2
3
4
5
6
7
-(void)dealloc {
 
_weak _typeof(self) weak_self = self;
 
NSLog(@“%@”, weak_self);
 
}

 

请问,当执行该方法时会出现审结果?为什么?

   崩溃。原因是不允许在dealloc中取weak self

  崩溃时在控制台打印出的信息时: 

1
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 的函数代码,找到问题所在。
weak_register_no_lock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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 有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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  在主线程执行       同步执行

 

posted @   极度恐慌_JG  阅读(399)  评论(0编辑  收藏  举报
编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!
历史上的今天:
2017-03-09 Mac环境下svn的使用(转)
点击右上角即可分享
微信分享提示