iOS懒人面试总结(持续更新)

面试题(主要通过OC举例,Swift语法相关待更新)

main函数解析

int main(int argc, char * argv[]) {
    @autoreleasepool {
        /**
         *  argc,argv是C标准main函数的参数,直接传递给UIApplicationMain进行相关处理即可;
         principalClassName:指定应用程序类,该类必须是UIApplication类或其子类。如果为nil,则用UIApplication类作为默认值
         delegateClassName:指定应用程序类的代理类,该类必须遵循UIApplicationDelegate协议,此函数会根据principalClassName创建UIApplication对象,根据delegateClassName创建一个delegate对象,并将该delegate对象赋值给UIApplication对象中的delegate属性。UIApplication对象会依次给delegate对象发送不同的消息,接着会创建应用程序的main runloop(事件循环),进行事件的处理(首先会调用delegate对象的 application:didFinishLaunchingWithOptions : ) 程序正常退出时这个函数才返回。
         UIApplication 对象管理事件循环和高层的应用行为
         应用委托是应用的核心,该对象与UIApplication对象联合起来负责对应用的初始化过程、状态迁移过程和很多高层的应用事件进行处理
         */
     return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}
复制代码

举例:GCD使用

1)串行队列

    dispatch_queue_t serialQueue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(serialQueue, ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                 NSLog(@">>>%d", i);// 此处做UI刷新,如排行榜头像顺序显示
            });
        });
    }
复制代码

 

或(使用dispatch_apply,不立即返回,打印执行结果是1243)

 

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_apply(10, queue, ^(size_t i) {
        NSLog(@"2");
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"3");
        });
    });
    NSLog(@"4");
复制代码

2)并行队列

// (参数一:线程优先,参数二:标记参数)

    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < count; i ++) {
        dispatch_async(globalQueue, ^{
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@">>>%d", i);// 此处做UI刷新
            });
        });
    }
复制代码

    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(10, queue, ^(size_t i) {
        NSLog(@"%@我开始执行 %zu times", [NSThread currentThread], i+1);
     });
    NSLog(@"done");
复制代码

3)多个异步任务,同时返回处理

    NSLog(@"全部开始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
    dispatch_group_async(group, queue, ^{
        sleep(4);
        NSLog(@"子线程1-----%@", [NSThread currentThread]);
    });
        
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"子线程2-----%@", [NSThread currentThread]);
    });
        
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部结束-----%@", [NSThread currentThread]);
    });
复制代码

4)多个请求,同时返回处理

    NSLog(@"即将开始多个请求");
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getBusinessDataWithSuccess:^(NSDictionary *result) {
            dispatch_group_leave(group);
            NSLog(@"请求1完成");
        } failure:^(NSError *error) {
            dispatch_group_leave(group);
        }];
    });
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getHomeConfigurationWithSuccess:^(NSDictionary *result) {
            dispatch_group_leave(group);
            NSLog(@"请求2完成");
        } failure:^(NSError *error) {
            dispatch_group_leave(group);
        }];
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"全部完成");
    });
复制代码

举例:核心动画

// 1、CAAnimation-抽象类、父类,控制持续时间和速度,不直接使用


// 2、CABasicAnimation-基本动画
CABasicAnimation *anima = [CABasicAnimation animationWithKeyPath:@"position"];
anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(a, b)];
anima.toValue = [NSValue valueWithCGPoint:CGPointMake(c, d)];
anima.duration = 1.0f;
[someView.layer addAnimation:anima forKey:@"positionAnimation"];


// 3、CAKeyframeAnimation-关键帧动画


// 4、CAAnimationGroup-动画组


// 5、CATransition-转场动画
复制代码

OC发射机制

获取类:[self class]

判断关系:isKindOfClass(判断是否是这个类或它的子类实例)、isMemberOfClass(判断是否是这个类的实例)

动态调用方法等:performSelector:(SEL)

线程之间的通信方式

performSelectorOnMainThread: withObject: waitUntilDone:
performSelector: onThread: withObject: waitUntilDone:
dispatch_get_global_queue(0,0)
dispatch_get_main_queue()
复制代码

OC可以多重继承吗

不行,C++可以,OC通过实现多个接口达到相似效果

id声明的对象有什么特性

运行时runtime特性,可以指向任意OC对象

原子(atomic)非原子(nonatomic)区别

atomic:

  • 是默认的
  • 线程安全,能在别的线程来访问这个属性之前,先执行完当前流程
  • 速度不快,因为要保证操作整体完成

nonatomic:

  • 不是默认的
  • 更快
  • 线程不安全
  • 如有两个线程访问同一个属性,会出现无法预料的结果

分类category与类扩展extension、继承的区别

类别 -只能新增方法(编译时categroy的内存布局已经确定,没有ivar,所以不能添加属性,需要重写set和get方法进行runtime属性关联);

类扩展 -可以新增属性(必须有类的源码才行);

继承 -可以增删改方法及属性;

tips:

多个category的调用顺序按照:Build Phases ->Complie Source 中的编译顺序

修饰符总结

基本数据类型 - assign(assign可修饰数据和对象、weak只对象;assign修饰对象释放后,指针不会置空);

NSString/Block - copy(用strong、copy修饰的区别,strong修饰时被赋值一个可变字符串对象,会强引用这个对象,当对象改变时,属性值也跟着变了);

delegate - weak(防止循环引用,无法释放delegate对象);

其他指针对象 - strong;

xib/storyboard - weak;

自定义UI - strong;

OC与JS交互:

-拦截url进行跳转

-JavaScriptCore(只适用于UIWebView)

-遵循协议:WKScriptMessageHandler(只适用于WKWebView)

OC与Swift特性及混编

特点:

OC:面向对象的C语言的延伸,具有runtime、runloop,MRC,ARC等特性;

Swift:继承OC面向对象的特点,具有简洁高效安全的语法特色,支持边写边看Playground/SwiftUI,以及泛型、可选型、元组、字符串枚举等特点;

混编:

OC调用Swift:import “ProjectName-Swift.h”

Swift调用OC:import “ProjectName-Bridging-Header.h”

Get和Post区别:

(1)post更安全(不会作为url的一部分,不会被缓存、保存在服务器日志、以及浏览器浏览记录中)

(2)post发送的数据更大(get有url长度限制)

(3)post能发送更多的数据类型(get只能发送ASCII字符)

(4)post比get慢(post多了一些额外字段,先发送head确认,再发送body)

什么是block

block是封装了函数调用及环境的OC对象,本质是结构体,内部也有isa指针。

block为什么用copy(retain)修饰

block默认是声明为栈变量的(栈区的特点就是创建的对象随时可能被销毁),为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆。

什么是isa指针

类似isKindOf,指向所属类的指针。对象—>类对象—>父类—>元类—>根类(NSObject)

设置信号量

1)dispatch_semaphore_create(0):创建一个Semaphore并初始化信号的总量;

2)dispatch_semaphore_wait(name, DISPATCH_TIME_FOREVER):可以使总信号量-1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行;

3)dispatch_semaphore_signal(name):发送一个信号,让信号总量+1;

消息转发机制(objc_msgSend(target, @selector(doSomething)))

-查找方法缓存:cache_t

-找实际方法:对象isa->类->父类->元类->基类,method_list

-动态方法解析:resolveClassMethod(类)、resolveInstanceMethod(对象);addMethodWithClass(动态添加方法补救)

-消息转发流程:

  快速转发:forwardingTargetForSelector(返回备用对象)

  标准转发:methodSignatureForSelector(SEL方法的签名)、forwardInvocation(创建备用对象看是否响应SEL,无法响应则doesNotRecognizeSelector)

weak实现原理

Runtime维护了一个weak表(hash表),用于存储指向某个对象的所有weak指针。Key是所指对象的地址,Value是weak指针。

1、初始化:objc_initWeak

2、添加:objc_storeWeak()

3、释放:clearDeallocating,循环遍历->weak指针 = nil->移除weak表记录

对象序列化和反序列化

model类实现NSCodeing协议:

-(void)encodeWithCoder:(NSCoder *)aCoder;

-(instancetype)initWithCoder:(NSCoder *)aDecoder;

运用:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:model];

Model *model = [NSKeyedUnarchiver unarchiveObjectWithData:data];

深浅拷贝简述

浅拷贝 复制指针,retainCount +1

深拷贝 复制内容,retainCount 不变

[不可变对象 copy] 不可变,浅拷贝

[不可变** mutableCopy] 可变,深拷贝

[可变** copy] 可变,深拷贝

[可变** mutableCopy] 可变,深拷贝

 

一对多代理如何实现?(多播代理)

将一对一的delegate,委托给一个单例,需要完成代理业务的界面实现单例对外的block/delegate等

autoreleasepool的原理和使用场景?

若干个autoreleasepoolpage组成的双向链表的栈结构,objc_autoreleasepoolpush、objc_autoreleasepoolpop、objc_autorelease

    使用场景:多次创建临时变量导致内存上涨时,需要延迟释放

autoreleasepoolpage的内存结构:4k存储大小

属性多读单写(多线程可读单线程写)如何实现?

使用线程锁:@synchronized、NSLock、dispatch_semaphore

AFN多线程通过什么管理?

NSOperationQueue

SDWebImage的cache原理?

存储在内存和硬盘中,可设置是否cache到硬盘。先查找内存,不存在,再查找硬盘;查找硬盘时,以URL的MD5值作为key;不存在,再执行下载;下载完毕保存内存和硬盘。

NSArray与NSSet的区别?

NSArray内存中存储地址连续,而NSSet不连续;

NSSet效率高,内部使用hash查找,NSArray查找需要遍历;

NSSet通过anyObject访问元素,NSArray通过下标访问;

对iOS类私有属性的访问和修改有两种方法:

KVC、Runtime(重写set、get,类似分类,进行属性关联)

写一个线程安全的单例模式

static id _instance;
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _instance = [super allocWithZone:zone];
   });
   return _instance;
}
 
+ (instancetype)sharedData {
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       _instance = [[self alloc] init];
   });
   return _instance;
}
// 遵循NSCopying协议
- (id)copyWithZone:(NSZone *)zone {
   return _instance;
}
复制代码

instancetype和id的异同

1)相同点

都可以作为方法的返回类型

2)不同点

①instancetype可以返回和方法所在类相同类型的对象,id只能返回未知类型的对象;

②instancetype只能作为返回值,不能像id那样作为参数

扩展知识点

静态库生成,支持真机和模拟器

1)shift+command+n -> framework

2)Edit Scheme -> Run -> Release

3)Build Setting -> Mach-O Type -> Static Library

4)Build Phases -> Headers -> Public

5)Coding&add files

6)command+b

7)lipo -create 真机路径 模拟器路径 -output 真机路径

内存讲解

iOS内存分为5个区:栈区,堆区,全局区,常量区,代码区(从左到右由高地址像低地址)。

  • 栈区stack:这一块区域系统会自己管理,我们不用干预,主要存一些局部变量,以及函数跳转时的现场保护。
  • 堆区heap:与栈区相对,这一块一般由我们自己管理,比如alloc,free的操作,存储一些自己创建的对象。
  • 全局区(静态区static):全局变量和静态变量都存储在这里,已经初始化的和没有初始化的会分开存储在相邻的区域,程序结束后系统会释放。
  • 常量区:存储常量字符串和const常量
  • 代码区:存储代码

lldb (gdb)常用的控制台调试命令?

1). p(print) 输出数据类型及对应值

2). po(print-object) 打印对象

3). expr(expression) 调试时动态执行表达式/赋值,并将结果打印出来

4). bt:打印调用堆栈

性能调优

耗电(intrument)、内存泄漏(analyze,Memery Leak)、启动速度(无用重复代码清理、framework优化、延迟、异步)、包体瘦身(冗余代码、减少xib、废弃文件、废弃图片)

工具:intrument、analyze、memery leak、断点调试

数据库增删改查

增:INSERT or REPLACE INTO t1 (a,b,c,d) VALUES (1,2,3,4)

删:DELETE FROM t1 WHERE userid=110

改:UPDATE t1  set name=‘abc’ WHERE userid=110

查:SELECT * FROM t1 LEFT JOIN t2 ON t1.userid=t2.userid WHERE userid NOT IN (SELECT userid FROM t3)  AND sex=1 order by datetime(createtime) DESC/ASC

数量查询:SELECT count(distinct userid) as someCount FROM t1 WHERE name=‘abc’

组件化

好处:代码清晰易用、解耦合

推荐模式:target-action

(组件—>target(中间件,封装了组件的调用)—>category(CTMediator分类,通过发射机制(NSClassFromString/NSSlectorFromString)提供runtime服务)—>外层调用)

中心思想:①、封装公共库和基础UI库 ②、独立业务模块化 ③、接口调用最小化

私聊历史消息翻页如何进行刷新?

开始更新-插入/删除行-结束更新

- (void)beginUpdates; 
 
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
 
- (void)endUpdates;
复制代码

举例:算法-快速排序

    NSMutableArray *arr = [NSMutableArray arrayWithObjects:@5,@2,@6,@7,@1,@3,@8,@4, nil];
    printf("排序前:");
    [self printArr:arr];
    [self quickSort:arr low:0 high:(int)arr.count-1];
    printf("排序后:");
    [self printArr:arr];


- (void)printArr:(NSArray *)arr {
    for (NSNumber *num in arr) {
        printf("%d ", num.intValue);
    }
    printf("\n");
}

- (void)quickSort:(NSMutableArray *)arr low:(int)low high:(int)high {
    // 1、安全判断
    if (arr == nil || arr.count == 0) {
        return;
    }
    if (low >= high) {
        return;
    }
 
    // 2、找到中间位索引、值
    int middle = low + (high - low)/2;
    int middleNum = [arr[middle] intValue];
 
    // 3、i从低位开始,j高位开始循环,与中值做比较符合条件调换位置
    int i = low;
    int j = high;
    while (i <= j) {
        // 升序,降序改为 >
        while ([arr[i] intValue] < middleNum) {
            i++;
        }
        // 升序,降序改为 <
        while ([arr[j] intValue] > middleNum) {
            j--;
        }
        if (i <= j) {
            [arr exchangeObjectAtIndex:i withObjectAtIndex:j];
            i++;
            j--;
        }
        printf("排序中:");
        [self printArr:arr];
    }
    
    // 4、继续下一轮比较
    if (low < j) {
        [self quickSort:arr low:low high:j];
    }
    
    if (high > i) {
        [self quickSort:arr low:i high:high];
    }
}
复制代码

沙盒结构:

1). Application:存放程序源文件,上架前经过数字签名,上架后不可修改。

2). Documents:常用目录,iCloud备份目录,存放数据。(这里不能存缓存文件,否则上架不被通过)

3). Library:

Caches:存放体积大又不需要备份的数据。(常用的缓存路径)

Preference:设置目录,iCloud会备份设置信息。

4). tmp:存放临时文件,不会被备份,而且这个文件下的数据有可能随时被清除的可能。

分类:
iOS
标签: