iOS那些坑

iOS面试题汇总(二)

2017-11-02  本文已影响13人  zgsddzwj

13.简单介绍下iOS中消息调用的过程

14.Runloop和线程有什么关系?关于Runloop你了解多少?

总的说来,Run loop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa 和 CoreFundation 都提供了 run loop 对象方便配置和管理线程的 run loop (以下都以 Cocoa 为例)。每个线程,包括程序的主线程( main thread )都有与之相应的 run loop 对象。

  • runloop 和线程的关系:
    线程和 RunLoop 之间是一一对应的,其关系是保存在一个全局的 Dictionary 里。线程刚创建时并没有 RunLoop,如果你不主动获取,那它一直都不会有。RunLoop 的创建是发生在第一次获取时,RunLoop 的销毁是发生在线程结束时。你只能在一个线程的内部获取其 RunLoop(主线程除外)。
    主线程的run loop默认是启动的。
    iOS的应用程序里面,程序启动后会有一个如下的main()函数
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
  • 重点是UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。
  • 对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
  • 在任何一个 Cocoa 程序的线程中,都可以通过以下代码来获取到当前线程的 run loop 。
  • NSRunLoop *runloop = [NSRunLoop currentRunLoop];

参考文档:深入理解Runloop

15.你了解source0、source1么??

CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef

具体原理参考上个题目的链接~~~

16.Runtime你了解么?实际使用中用到了

具体使用场景:
1.利用关联对象给category添加成员属性。
2.在load做方法替换,添加打点信息。
3.消息拦截、消息转发。

下面这篇文章讲解的够详细的,直接转载了
[iOS] runtime 的使用场景--实战篇

17.如何扩大button的点击区域
-首先添加一个UIButton的category来重写hitTest,然后通过为分类添加的一个属性来扩大按钮的响应区域

UIButton+EnlargeHitArea.h
@interface UIButton (Extensions)
@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end

UIButton+EnlargeHitArea.m

#import "UIButton+EnlargeHitArea.h"
#import <objc/runtime.h>

@implementation UIButton (EnlargeHitArea)

@dynamic hitTestEdgeInsets;

static const NSString *KEY_HIT_TEST_EDGE_INSETS = @"HitTestEdgeInsets";

-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets
 {
    NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)];
    objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(UIEdgeInsets)hitTestEdgeInsets
{
    NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
    if(value)
    {
        UIEdgeInsets edgeInsets;
        [value getValue:&edgeInsets]; 
        return edgeInsets;
    }
    else  
    {
        return UIEdgeInsetsZero;
    }
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
 {
    if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) 
    {
        return [super pointInside:point withEvent:event];
    }

    CGRect relativeFrame = self.bounds;
    CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);

    return CGRectContainsPoint(hitFrame, point);
}

@end

通过设置按钮分类的hitTestEdgeInsets属性扩大按钮的响应范围

#import "UIButton+EnlargeHitArea.h"
[button setHitTestEdgeInsets:UIEdgeInsetsMake(-10, -10, -10, -10)];

18.Extension和Category区别,及对应的用法。

延伸:
1)、在类的+load方法调用的时候,我们可以调用category中声明的方法么?
2)、这么些个+load方法,调用顺序是咋样的呢?
答案是:
1)、可以调用,因为附加category到类的工作会先于+load方法的执行
2)、+load的执行顺序是先类,后category,而category的+load执行顺序是根据编译顺序决定的。

参考:
类别(Category)与类扩展 (Extension)的区别
深入理解Objective-C:Category

19.[UIApplication sharedApplication].keyWindow 添加视图无效,解决方案。(keyWindow和delegate.window区别)

在rootViewController中的viewDidLoad:方法中调用[[UIApplication sharedApplication].keyWindow addSubview:]
发现无效
调试发现[UIApplication sharedApplication].keyWindow 为nil
因为这个时候appdelegate中的keywindow还没有创建成功
我们可以用[[[UIApplication sharedApplication] delegate] window]
代替[UIApplication sharedApplication].keyWindow
亲测可以
这个问题在iOS7中很常见,iOS8中苹果就解决了这个弊端

UIWindow *window = [[[UIApplication sharedApplication] delegate] window]
[window addSubview:]

keyWindow与delegate中的window其实是一样的,keyWindow的存在的意义,其实就是为了说明当前的window接管了这个控制器的view而已,你可以在keyWindow上加载你自己的建立的view了。
参考:http://www.itcto.com.cn/post/2017/02/01/keywindow-delegate-window-1612161901139.aspx

20.LayoutSubViews和drawrect分别在什么时候调用

这个方法,默认没有做任何事情,需要子类进行重写 。 系统在很多时候会去调用这个方法:

1.初始化不会触发layoutSubviews,但是如果设置了不为CGRectZero的frame的时候就会触发。
2.addSubview会触发layoutSubviews
3.设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化
4.滚动一个UIScrollView会触发layoutSubviews
5.旋转Screen会触发父UIView上的layoutSubviews事件
6.改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

这个方法是用来重绘的。

drawRect在以下情况下会被调用:

1、如果在UIView初始化时没有设置rect大小,将直接导致drawRect不被自动调用。drawRect调用是在Controller->loadView, Controller->viewDidLoad 两方法之后掉用的.所以不用担心在控制器中,这些View的drawRect就开始画了.这样可以在控制器中设置一些值给View(如果这些View draw的时候需要用到某些变量值).
2、该方法在调用sizeToFit后被调用,所以可以先调用sizeToFit计算出size。然后系统自动调用drawRect:方法。
3、通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
4、直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect不能为0。
以上1,2推荐;而3,4不提倡

drawRect方法使用注意点:

1、若使用UIView绘图,只能在drawRect:方法中获取相应的contextRef并绘图。如果在其他方法中获取将获取到一个invalidate的ref并且不能用于画图。drawRect:方法不能手动显示调用,必须通过调用setNeedsDisplay 或者 setNeedsDisplayInRect,让系统自动调该方法。2、若使用calayer绘图,只能在drawInContext: 中(类似于drawRect)绘制,或者在delegate中的相应方法绘制。同样也是调用setNeedDisplay等间接调用以上方法
3、若要实时画图,不能使用gestureRecognizer,只能使用touchbegan等方法来掉用setNeedsDisplay实时刷新屏幕

本人QQ:297959735 邮箱:zgsddzwj@163.com,欢迎提意见。

上一篇 下一篇

猜你喜欢

热点阅读