美文网首页
ios知识点

ios知识点

作者: 弹吉他的少年 | 来源:发表于2018-09-01 16:45 被阅读15次

基础:

1、如何令自己所写的对象具有拷贝功能?

  • 实现NSCoping协议。如果自定义的对象分为可变版本与不可变版本,那么就要同时实现 NSCopying与NSMutableCopying协议。
@protocol NSCopying

- (id)copyWithZone:(nullable NSZone *)zone;

@end

@protocol NSMutableCopying

- (id)mutableCopyWithZone:(nullable NSZone *)zone;

@end

2、说说你理解weak属性?

  • 当ARC发现count大于0,就不会释放资源,而weak不会使count增加,weak只是简单的把源对象的指针拿过来用,不影响ARC的release动作,在使用weak的时候要确保使用期间源对象是有效的,也就是说必须在源对象的生命周期内使用weak引用,否则的话会导致程序异常。
    • 初始化时:runtime会调用objc_initWeak函数,初始化一个新的weak指针指向对象的地址。
    • 添加引用时:objc_initWeak函数会调用 objc_storeWeak() 函数,objc_storeWeak() 的作用是更新指针指向,创建对应的弱引用表。
    • 释放时,调用clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有weak指针地址的数组,然后遍历这个数组把其中的数据设为nil,最后把这个entry从weak表中删除,最后清理对象的记录。

3、UIView和CALayer是什么关系?

  • UIView继承自UIResponder,能接收并响应事件,负责显示内容的管理
  • CALayer继承自NSObject,不能响应事件,负责显示内容的绘制
  • UIView本身更像是一个CALayer的管理器,访问它的跟绘图和跟坐标有关的属性,例如frame,bounds等,实际上内部都是在访问它所包含的CALayer的相关属性

4、@synthesize 和 @dynamic 分别有什么作用?

  • synthesize,编译器自动生成setter和getter的方法,在你没有手动去实现这两个方法时。
  • dynamic,告诉编译器你会动态生成setter和getter方法,不会要编译器帮你生成。

5、动态绑定

  • 运行时确定要调用的方法
  • 动态绑定将调用方法的确定也推迟到运行时。在编译时,方法的调用并不和代码绑定在一起,只有在消实发送出来之后,才确定被调用的代码。通过动态类型和动态绑定技术,您的代码每次执行都可以得到不同的结果。运行时因子负责确定消息的接收者和被调用的方法。运行时的消息分发机制为动态绑定提供支持。当您向一个动态类型确定了的对象发送消息时,运行环境系统会通过接收者的isa指针定位对象的类,并以此为起点确定被调用的方法,方法和消息是动态绑定的。而且,您不必在Objective-C 代码中做任何工作,就可以自动获取动态绑定的好处。您在每次发送消息时,特别是当消息的接收者是动态类型已经确定的对象时,动态绑定就会例行而透明地发生。

6、Category(类别)、 Extension(扩展)和继承的区别

  • 类别(Category)
    • 为已有的类添加新方法(不能添加实例变量,可以封装属性)
    • 语法: @interface 已有类 (类别名)
    • 习惯性将类别的文件名命名为"类名+类别名.h"
    • 能将一个类的代码分散到多个文件对中
    • 对类进行模块化设计
    • 调用私有方法
  • 扩展Extension
    • 可以添加实例变量
    • 语法: @interface 已有类 ()
    • 相当于匿名的类别
    • 将类的声明部分分散到多个文件,但实现文件只有一个
    • 扩展的文件名命名为"类名_扩展名.h"
  • 类别不能申明新的实例变量,扩展可以
  • 类别可以将类的声明和实现部分分散到不同的文件中
  • 扩展只能将类的声明部分分散到不同的文件中
  • 继承必须子类化,可以增加/重写方法,可以申明新的实例变量。

7、为什么代理要用weak?代理的delegate和dataSource有什么区别?block和代理的区别?

  • 防止循环引用。例如View有一个协议,需要一个代理实现回调。一个Controller添加这个View,并且遵守协议,成为View的代理。如果不用week,用strong,Controller ->View -> delegate -> Controller,就循环引用了。
  • delegate偏重于与用户交互的回调,有那些方法可以供我使用,例如UITableviewDelegate;
  • dataSource偏重于数据的回调,view里面有什么东西,属性都是什么,例如UITableviewDatasource;
  • Block: Objective-C/Swift中对闭包(closure)的实现,广泛使用在回调上。
  • Delegate: Cocoa的基本设计模式之一,面向协议(protocol)的编程,广泛使用在回调和对象间传值。

8、id和NSObject*的区别

  1. NSObject包含了一些其他的方法,需要实现NSObject协议,可以用NSObject来表示id,但是不能用id来表示NSObject
  2. id关键字在编译的时候不会被检查,而NSObject在编译的时候被被检查是否含有一些错误的方法
  3. id可以是任何对象,包括不是NSObject的对象
  4. 定义id的时候不需要*,而定义NSOject的时候需要。

9、使用系统的某些block api(如UIView的block版本写动画时),是否也考虑引用循环问题?

  • 所谓“引用循环”是指双向的强引用,所以那些“单向的强引用”(block 强引用 self )没有问题。
  • 但如果你使用一些参数中可能含有 ivar 的系统 api ,如 GCD 、NSNotificationCenter就要小心一点:比如GCD 内部如果引用了 self,而且 GCD 的其他参数是 ivar,则要考虑到循环引用。

10、用@property声明的NSString(或NSArray,NSDictionary)经常使用copy关键字,为什么?如果改用strong关键字,可能造成什么问题?

  • copy 深复制,新建一个对象,并将源对象的内容复制给新对象,复制后有两个对象。
  • strong 浅复制,复制对象的引用,复制后仍只有一个对象,但是有两个指针指这个对象。
  • NSString,NSArray这些类拥有Mutable的子类。当你给属性赋值的时候,用的是不可变类型,其实strong和copy没有什么差别,危险的地方在于,如果用于暴露的接口,可能会被外部的变量改变。
    当赋值Mutable对象时,copy会进行一次深copy,重新分配一块内存,strong是直接引用源内存。

11、static有什么作用?

  • 用static声明局部变量,使其变为静态存储方式(静态数据区),作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
  • 用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。
  • 函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
  • 在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问;
  • 在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
  • 在类中的 static 成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;
  • 在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。

12、预处理指令#include中的<>和””的区别是什么?

  • 尖括号指示预处理器从系统路径开始搜索要包含的文件
  • 双引号指示预处理器先从用户路径开始搜索要包含的文件,如果搜索不到再搜索系统路径

13、预处理指令#include和#import的区别是什么?

  • import是Objective-C包含头文件的关键字,
  • include是C/C++包含头文件的关键字;
  • 使用#import,头文件会自动只包含一次,不会重复包含,可以解决头文件相互包含的问题。

14、变量有哪些种类? (5种存储类)

15、请描述静态区、栈、堆中定义的变量的区别

  • 静态区中的变量在编译期就已经分配好了内存,其生命周期与应用程序生命周期相同;
  • 栈中的变量都是自动存储类的变量,在程序运行的过程中动态的申请内存,其生命周期与函数栈帧的生命周期相同,其内存的申请与释放由编译器负责;
  • 在堆中申请和释放内存由用户决定。没有声明在堆中的变量,往往通过声明一个指针变量来访问堆中的内存。

16、堆和栈的区别是什么?

  • 所处位置:栈位于应用程序内存空间最高位处,堆与静态区高位处相邻
  • 扩展方向:栈向低地址扩展,堆向高地址扩展
  • 管理方式:栈由编译器自动管理,堆由程序员自行控制
  • 内存分布:栈是一块连续的内存空间,堆是不连续的内存空间(链式结构)
  • 内存大小:Mac OS X 64bit中,栈空间默认为8M,堆受限于虚拟内存与位数
  • 碎片问题:栈不会产生碎片,堆会造成碎片
  • 执行效率:栈效率高(有专门的指令和寄存器),堆效率低

17、NSInteger和CGFloat是什么数据类型?

  • NSInteger在32位系统中为int,在64位系统中为long
  • CGFloat在32位系统中为float,在64位系统中为double

18、Objective-C支持多继承吗?如果没有用什么代替?

  • 多继承是指一个类从多于一个直接基类派生类的能力,Objective-C不支持多继承。
  • 可以用下面三种方法代替:
    • 消息转发
    • 协议/委托
    • 类别

19、为什么说Objective-C是动态语言?

  • 多态,即数据类型在编译期和运行时可能会不一致,是在运行时才最终决定。

20、简述Objective-C中对象的内存管理

  • Objective-C中对象的内存管理采用了引用计数。当一个对象被他人引用时,引用计数会加一,alloc、retain、copy都会造成引用计数加一;当一个对象被release时,引用计数会减一,当引用计数减到零时,才会调用dealloc真正的释放掉对象。

21、为什么很多类的delegate属性用的是assign而不是retain/strong?

  • 循环引用就是多个对象之间形成了环装引用。
  • 要打破循环引用,所以需要声明成assign
  • 例如:
    • UITableViewController管理了一个UITableView,并且UITableView的delegate和datasource为UITableViewController。
  • 如果delegate和datasource使用了retain/strong内存管理方式,则:
    • 当创建UITableViewController实例时,retainCount=1
    • 当创建所管理的UITableView实例时会设置delegate和datasource,这时retainCount=3
    • 当释放UITableViewController实例时,retainCount减1,这时retainCount=2
    • 这就造成了UITableViewController无法被释放掉

22、frame和bounds有什么不同?

  • frame:本视图在父视图坐标系中的位置和大小
  • bounds:本视图在自身坐标系中的位置和大小

23、简述单例模式

  • 单例模式是一种常见的软件设计模式,它能保证在整个应用程序生命周期内,单例类的实例至多只有一个,并能通过一个全局入口对这个实例进行访问。
  • 拓展:单例的写法
    h文件中声明
+ (instancetype)shareObject;

m文件中实现

// 单例(在m文件中实现)
+ (instancetype)shareObject{
    static Model *model = nil;
    // 在整个程序的生命期只会被执行一次
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        model = [[super allocWithZone:NULL] init];
    });
    return model;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    //使用加锁的方式,保证只分配一次存储空间
    @synchronized(self) {
        if (_instance == nil) {
            _instance = [super allocWithZone:zone];
        }
    }
    return _instance;
}

24、简述tableView中cell的重用机制

  • 在UITableView的头文件中,有两个属性,分别为visiableCells和reusableTableCells。visiableCells属性保存当前显示的cells,reusableTableCells则作为cells的可重用池。
  • 当一个cell不被显示时,会被放入可重用池中。当UITableView通过委托方法tableView: cellForRowAtIndexPath:来获取新的cell时,应该先通过dequeueReusableCellWithIdentifier:方法从可重用池中获取cell,如果获取不到再进行创建。
  • 这样无论UITableView中多少行的数据,最多也只需创建可显示数量的cell,避免过多的创建cell对象,极大的节省了内存和提高了运行的速度。

25、响应者链是如何传递的?

  • 一次完整的触摸事件的传递响应过程如下图,其中从第一响应者开始,任何环节只要实现了触摸处理,则会中断传递;如果传递到了AppDelegate还没有任何响应者处理触摸事件,则会丢弃事件。

26、__block和__weak修饰符的区别?

  • __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
  • __weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
  • __block对象可以在block中被重新赋值,__weak不可以。

__weakViewController* weakSelf =self;
GCD里面用 __weak 防止内存释放不了,循环引用。

底层:

27、main()之前的过程有哪些?

  • dyld 开始将程序二进制文件初始化
  • 交由ImageLoader 读取 image,其中包含了我们的类,方法等各种符号(Class、Protocol 、Selector、 IMP)
  • 由于runtime 向dyld 绑定了回调,当image加载到内存后,dyld会通知runtime进行处理
  • runtime 接手后调用map_images做解析和处理
  • 接下来load_images 中调用call_load_methods方法,遍历所有加载进来的Class,按继承层次依次调用Class的+load和其他Category的+load方法
  • 至此 所有的信息都被加载到内存中
  • 最后dyld调用真正的main函数

注意:dyld会缓存上一次把信息加载内存的缓存,所以第二次比第一次启动快一点

28、objc在向一个对象发送消息时,发生了什么?

  • objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的

29、静态库的原理是什么?你有没有自己写过静态编译库,遇到了哪些问题?

  • 静态库 一般都是以 .a 或者 .framework 形式存在。
  • 使用静态库的好处:模块化分工合作、可重用、避免少量改动导致大量的重复编译链接。

30、Runloop是什么?

  • RunLoop和线程的关系:
    • RunLoop是用来管理线程的,每个线程对应一个RunLoop对象。我们不可以去创建当前线程的RunLoop对象,但是我们可以去获取当前线程的RunLoop。RunLoop就是来监听该线程有无事件发生,如果有就工作,如果没有就休眠。
    • 主线程的RunLoop对象默认开启,其他线程默认不开启。
  • Runloop与线程:
  1. Runloop和线程的关系:一个Runloop对应着一条唯一的线程。
  2. Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建。
  3. Runloop的生命周期:在第一次获取时创建,在线程结束时销毁。

UITrackingRunLoopMode: (优先切换!!)这个模式就是当UI事件交互的时候Runloop切换到的模式!!!
场景:这一模式优先级最高,当UI事件交互的时候,都会优先切换到这一模式。
NSDefaultRunLoopMode:Runloop的默认模式!只要有事件就处理!
场景:默认模式,只要有事件就会自动切换到此模式。
NSRunLoopCommonModes:占位符!!(在默认下和UITrackingRunLoopMode下!)
场景:这个主要用在添加一个NSTimer到RunLoop中。是一个tag,本质上不是一个Mode,默认NSDefaultRunLoopMode和 NSTrackingRunLoopMode都绑定这个tag。

31、OC完整的消息转发机制

  • 首先根据receiver对象的isa指针获取它对应的class
  • 优先在class的cache查找message方法,如果找不到,再到
    methodLists查找
  • 如果没有在class找到,再到super_class查找
  • 一旦找到message这个方法,再依据receiver 中的self 指针找到当前的对象,调用当前对象的具体实现的方法(IMP),然后传递参数,调用实现方法。

32、以+ scheduledTimerWithTimeInterval...的方式触发的timer,在滑动页面上的列表时,timer会暂定回调,为什么?如何解决?

  • 在主线程中以+scheduledTimerWithTimeInterval...的方式触发的timer 默认是运行在 NSDefaultRunLoopMode 模式下的,当滑动页面上的列表时,进入了 UITrackingRunLoopMode 模式,这时候 timer 就会停止可以修改 timer 的运行模式为 NSRunLoopCommonModes,这样定时器就可以一直运行了
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerTick:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 

33、如何手动触发一个value的KVO
键值观察通知依赖于NSObject的两个方法:willChangeValueForKey:和didChangeValueForKey:。

  • 在一个被观察属性发生改变之前,willChangeValueForKey:一定会被调用,这就会记录旧的值。
  • 而当改变发生后,didChangeValueForKey:会被调用,继而objectServeValueForKey:ofObject:change:content:也会被调用。
    如果可以手动实现这些调用,就可以实现“手动触发”了。

34、假设有一个字符串aabcad,请写一段程序,去掉字符串中不相邻的重复字符串,即上述字符串处理之后的输出结果为:aabcd

NSMutableString * str1 = [[NSMutableString alloc] initWithFormat:@"aabcad"];
    for (int i = 0; i < str1.length - 1; i++) {
        for (int j = i + 1; j < str1.length ; j++) {
            // 由于字符的特殊性  无法使用 字符串 isEqualToString 进行比较 只能转化为ASCII 值进行比较  所以 需要加 unsigined 修饰
           unsigned char a = [str1 characterAtIndex:i];
           unsigned char b = [str1 characterAtIndex:j];
            if (a == b) {
                if (j - i > 1) {
                    // NSRange:  截取字符串   {j, 1} j: 第一个字符开始  1: 截取几个字符
                    NSRange  range = {j, 1};
                   [str1 deleteCharactersInRange:range];
                    j = i--;
                }
            }
        }
    }
    NSLog(@"------ %@-------", str1);

线程:

35、ios中有几种实现多线程的方法?

  • NSThread、NSOperation和GCD
  • 线程安全(加锁、可修改类型是非线程安全的)、主线程更新界面

36、ViewController生命周期

  • viewDidLoad在view 从nib文件初始化时调用,
  • loadView在controller的view为nil时调用。此方法在编程实现view时调用,view 控制器默认会注册memory warning notification,当view controller的任何view 没有用的时候,
  • viewDidUnload会被调用,在这里实现将retain 的view release,如果是retain的IBOutlet view 属性则不要在这里release,IBOutlet会负责release 。

37、dispatch_barrier_async的作用是什么?

  • dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

38、如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)

/**
 * 队列组 dispatch_group_notify
 */
- (void)groupNotify{
    NSLog(@"group---begin");
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 加载图片
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 加载图片
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
        // 合成图片
    });
    NSLog(@"group---end");
}

http:

39、http与https的区别?

  • HTTPS和HTTP的区别主要如下:

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

40、服务器能否知道APNS推送后有没有到达客户端的方法?

  • APNS是苹果提供的远程推送的服务,APP开发此功能之后,用户允许推送之后,服务端可以向安装了此app的用户推送信息。但是APNS推送无法保证100%到达。
    目前关于APNS苹果更新了新的策略,即 APNS/HTTP2.
    如果服务器像APNS服务器推送信息之后,服务器能够接收到APNS是否真的成功像客户端推送成功了某个信息。

相关文章

网友评论

      本文标题:ios知识点

      本文链接:https://www.haomeiwen.com/subject/dzsuwftx.html