iOS开发细碎知识点总结一

本文总结了iOS开发中的各种知识点,包括Block循环引用的解决办法、网络拦截、多APP通信、Web存储、图片资源管理、内存管理、HTTP与HTTPS的区别、线程间通信、网络请求优化、缓存策略、类加载与初始化、KVC与KVO、网络请求的取消、断点续传的实现、线程死锁原因和解决方法、Python特点、UIKit Dynamics与Animation的区别、HTTP状态码、TCP的慢开始与快恢复等。文章内容丰富,涵盖了iOS开发的多个重要方面。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、写出ios方法获取ios内存使用情况
1.获取当前设备可用内存及所占内存的头文件

#import <sys/sysctl.h>
#import <mach/mach.h>

2.获取当前设备可用内存(单位:MB)

- (double)availableMemory

{

  vm_statistics_data_t vmStats;
  mach_msg_type_number_t infoCount =HOST_VM_INFO_COUNT;

  kern_return_t kernReturn = host_statistics(mach_host_self(),                                         HOST_VM_INFO,                                          (host_info_t)&vmStats,                                         &infoCount);
  if (kernReturn != KERN_SUCCESS) {
    return NSNotFound;
  }
  return ((vm_page_size *vmStats.free_count) /1024.0) / 1024.0;

}


3.获取当前任务所占用的内存(单位:MB)

- (double)usedMemory

{

  task_basic_info_data_t taskInfo;

  mach_msg_type_number_t infoCount =TASK_BASIC_INFO_COUNT;

  kern_return_t kernReturn =task_info(mach_task_self(), 

                                     TASK_BASIC_INFO, 

                                     (task_info_t)&taskInfo, 

                                     &infoCount);

 

  if (kernReturn != KERN_SUCCESS

      ) {

    return NSNotFound;

  }

  

 return taskInfo.resident_size / 1024.0 / 1024.0;

}

二、什么是安全释放?
答:置为nil再释放
三、什么是序列化和反序列化?
答:序列化是把对象转化成字节序列的过程,反序列化是把字节序列恢复成对象,序列化和反序列化可以将对象写入到文件或者数据库里
四、获取一台设备唯一标示的方法?
1.UDID,UDID全称是Unique Device Identifier,顾名思义,他就是苹果iOS设备的唯一识别码,他由40个字符的字母和数字组成。(已越狱手机不适用)

代码中获取UDID:自从iOS5之后,苹果就禁止了通过代码访问UDID,在这之前,可以使用[[UIDevice cuurrent] uniqueIdenfier] 这个方法来获取某设备UDID。苹果提供了一个参数identifierForVendor来替代原来UDID的作用。通过代码实现如下:


NSUUID *uuid = [UIDevice currentDevice].identifierForVendor;

这个identifierForVendor是应用和设备两者都有关的
2.UUID,Universally Unique Identifier,通用唯一标识符,为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间、随机或者伪随机数、时许等元素生成UUID算法。例如,你可以在第一次运行应用时生成一个UUID,然后存储在Keychain中。这样即使应用被删除并重新安装,只要Keychain没有被清除,这个UUID就会保持不变。

最简单获取UUID的代码如下:

// 循环10次,每一次打印的值都是不一样的,当然循环的再多,这个值永远不会出现两个一样的值。
NSString *uuid = [NSUUID UUID].UUIDString;

苹果公司建议使用UUID为应用生成唯一标识字符串。虽然UUID是官方提出的一种替代UDID的建议方案。但系统升级后UUID可能会发生变化。

3.MAC Address
APP中需要一个能够标识设备唯一性的ID怎么办?
1.Mac地址+bundle_id
2.推送token+bundle_id
五、ios类是否可以多继承,如果没有,那可以使用其他方法实现吗?简述实现过程
答:不可以多继承,使用protocol实现
六、Block和函数指针的区别
答:1. 函数指针是对一个函数地址的引用,这个函数在编译的时候就已经确定了。Block是一个函数对象,是在程序运行过程中产生的。
七、怎样判断某个cell是否显示在当前屏幕上?

1. 使用indexPathsForVisibleRowsUITableView	
   有一个indexPathsForVisibleRows方法,它返回一个数组,包含了当前屏幕上所有可见的cell的索引路径。你可以通过比较这个数组和你的cell的索引路径来决定它是否可见。
- (BOOL)isCellVisible:(NSIndexPath *)indexPath {
    NSArray<NSIndexPath *> *visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
    return [visibleIndexPaths containsObject:indexPath];
}
2. 使用rectForRowAtIndexPath:
   rectForRowAtIndexPath:方法获取指定索引路径的cell的矩形区域,然后判断这个矩形区域是否与屏幕的可见区域相交。
- (BOOL)isCellVisibleAtIndexPath:(NSIndexPath *)indexPath {
    CGRect cellRect = [self.tableView rectForRowAtIndexPath:indexPath];
    CGRect visibleRect = CGRectMake(0, self.tableView.contentOffset.y, self.tableView.bounds.size.width, self.tableView.bounds.size.height);
    return CGRectIntersectsRect(cellRect, visibleRect);
}
3. 使用visibleCells属性
   UITableView还有一个visibleCells属性,它返回一个数组,包含了当前屏幕上所有可见的cell对象。你可以通过遍历这个数组并比较对象来检查你的特定cell是否在其中。
   - (BOOL)isCellVisibleWithCell:(UITableViewCell *)cell {
    NSArray<UITableViewCell *> *visibleCells = self.tableView.visibleCells;
    return [visibleCells containsObject:cell];
}
4.使用convertRect:toView:方法
如果你想要更精确地判断一个cell是否完全或部分可见,你可以将cell的frame转换为相对于屏幕的坐标,然后检查这个坐标是否在屏幕的可见区域内。
- (BOOL)isCellPartiallyVisible:(UITableViewCell *)cell {
    CGRect cellFrameInWindow = [self.tableView convertRect:cell.frame toView:nil]; // 或者使用[self.tableView convertRect:cell.frame toView:[UIApplication sharedApplication].keyWindow] 如果在AppDelegate中调用此方法
    CGRect screenBounds = [UIScreen mainScreen].bounds;
    return CGRectIntersectsRect(cellFrameInWindow, screenBounds);
}

八、cocoa Touch 提供了几种core?
答:常用3中: 1.Core Animation,2.Core Audio,3.Core Data
Cocoa实际上有三个框架组成:
1.Foundation框架 2.Core Data框架 3.AppKit框架
Cocoa Touch 有三个框架组成:
1.Foundation框架 2.Core Data框架 3.UIKit框架
九、自动释放池什么时候创建,什么时候销毁?
1.启动runloop时候第一次创建
2.runloop退出的时候最后一次销毁
3.其他时候的创建和销毁:当runloop即将睡眠时销毁之前的释放池,重新创建一个新的。
十、NSMutableArray用copy修饰会出现什么问题?
添加,删除,修改数组内的元素的时候,程序会因为找不到对应的方法而崩溃,因为copy就是复制了一个不可变NSArray的对象。
十一、@property的本质是什么?ivar、getter、setter、如何生成并添加到这个类中的?
答:本质:@property = ivar+getter+setter;(实例变量+getter方法+setter方法)
在编译期自动生成getter、setter,还自动向类中添加适当类型的实例变量,也可以用@synthesize 语法来指定实例变量的名字。
十二、@synthesize和@dynamic分别有什么用?
答:
1.@property 有两个对应的词,一个@synthesize ,一个时@dynamic。如果@synthesize和@dynamic都没有写,那么默认的就是@synthesize var = _var;
2.@synthesize 的语意时如果你没有手动实现setter和getter方法,那么编译器会自动给你加上者两个方法。
3.@dynamic 告诉编译器:属性的setter和getter方法是由用户自己实现,不自动生成(当然对于readonly的属性只需提供getter即可)。假如一个属性被声明为@dynamic var,然后你没有提供@setter方法和@getter方法,编译的时候没有问题,但是程序运行到instance.var = someVar,由于缺少setter方法会导致程序崩溃,或者当运行到someVar=var时,由于缺少getter方法同样会导致崩溃。编译时没有问题,运行时才执行相应的方法,这就是所谓的动态绑定。
十三、ARC下不显式指定任何属性关键词时,默认的关键词都有哪些?
答:
1.基本数据类型:atomic、readwrite、assing
2.普通的OC对象,automic、readwrite、strong
十四、14.NSString、NSArray、NSDictionary经常使用copy修饰,如果改为strong会产生什么问题?

@property (nonatomic,strong)NSString *myStr1;
@property (nonatomic,copy)NSString *myStr2;

     NSMutableString *string = [[NSMutableString alloc] initWithString:@"123"];
     self.myStr1 = string;
     self.myStr2 = string;
     NSLog(@"stringp = %p",string);
     NSLog(@"myStr1 = %p",self.myStr1);
     NSLog(@"myStr2 = %p",self.myStr2);
      2021-11-01 09:43:31.025652+0800 MianShiTi[1780:113421] stringp = 0x600000c36be0
     2021-11-01 09:43:31.025771+0800 MianShiTi[1780:113421] myStr1 = 0x600000c36be0
     2021-11-01 09:43:31.025855+0800 MianShiTi[1780:113421] myStr2 = 0xb4e82055cbf47a80

结论:如果使用了strong,指针指向不变还是这个对象,只进行浅拷贝(指针拷贝),没有产生新对象,用copy修饰,会进行深拷贝(内容拷贝),产生新对象。
十五、Objc中向一个nil对象发送消息将会发生什么?
因为OC是通过objc_msgsend进行消息发送来实现的,
相对于C和C++来说,对于空指针会引起程序crash,

 Person *p = [[Person alloc] init];
 p = nil;
 [p gotoJob];
 程序正常运行

OC中,objc_会通过判断self来决定是否发送消息,如果self为nil,那么selector也会为空,直接返回,不会出现问题。
注意:(NSNull除外,【NSNull null】对象发送消息时,会crash)
十六、下面的代码输出什么?
定义一个Son类继承Father
1.

@implementation Son
- (instancetype)init {
    
    self = [super init];
    if (self) {
        NSLog(@"%@",NSStringFromClass([self class]));
        NSLog(@"%@",NSStringFromClass([super class]));
    }
    return self;
    
}
@end
答案是:
Son
Son
Son *s = [[Son alloc] init];
NSLog(@"%@",NSStringFromClass([s.superclass class]));
答案是:
Father
    

解析:
这个题目主要考察OC中对self和super的理解,super是一个关键字,它本质是一个编译器标示符,和self是指向的同一个消息接收者!

更详细的解释self和super关系
十七、描述对象的内存销毁事件表
1.调用 -release :引用计数为0
2.子类 调用-dealloc
3.NSobject 调用-dealloc
4.调用object_dispose()
十八、_objc_msgForward函数是做什么的,直接调用它会发生什么?
答:
当一个对象发送一个消息,但他并没有实现的时候,_Objc_msgForward会尝试做消息转发。
在消息传递过程中,Objc_msgSend的动作比较清晰:首先在Class中的缓存中查找IMP,如果没找到,则向父类的Class查找。如果一直查找到根类仍旧没有实现,则用_Objc_msgForward函数指针代替IMP,执行这个IMP。
_objc_msgForward 消息转发做的几件事:
1.调用resolveInstanceMethod方法,允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的动作。
2.调用forwardingTargetForSelector方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非nil对象。否则返回nil,继续下面的动作,注意,这里不要返回self,否则会形成死循环。
3.调用methodSignatureForSelector方法,尝试获取一个方法签名。如果获取不到,则直接调用doesNotRecognizeSeletor抛出异常。如果能获取,则返回一个nil:创建一个NSINvocation并传给forwardInvocation。
4.调用forwardInvocation方法,将第三步获取到的方法签名包装成Invocation传入如何处理就在这里面了,并返回nil。
5.调用doesNotRecognizeSelector方法,默认的实现是抛出异常,如果第三步没能获得这一个方法签名,执行该步骤。
注:如果用不好会直接导致程序Crash,但是如果用的好,做很多事情,比如JSPatch、RAC(ReactiveCocoa)。
十九、能否向编译后的类中添加实例变量?能否向运行时创建的类中添加实例变量?
答:不能向编译后的类中添加实例变量,可以向运行时创建的类中添加实例变量。
二十、BAD_ACCESS在什么情况下出现?
答:1.访问了野指针。(访问已经释放的对象的成员变量或者发消息)。
2.死循环。
二十一、autoreleasepool是什么?NSAutoreleasePool是什么?
答:NSAutoreleasePool实际上是一个对象引用计数自动处理器,在官方文档中被称为是一个类。
每一个线程都会维护自己的autoreleasepool堆栈,每一个autoreleasepool只对应一个线程。当你使用ARC时,就必须将NSAutoreleasePool的地方换成@autoreleasepool。
苹果是怎样实现autoreleasepool的?
答:
autoreleasepool 以一个队列数组的形式实现,主要通过下列三个函数完成
1.objc_autoreleasepoolPush
2.objc_autoreleasepoolPop
3.objc_autorelease
看函数名就可以知道,对autorelease分别执行了push和pop操作,销毁对象时执行release操作。
autorelease的原理是什么?
答:autorelease实际上只是把对release的调用延迟了,对于每一个autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中所有的Object会被调用Release。
autorelease的对象何时释放?
答: 对于autorelease pool本身,会在如下两个条件发生时候释放:
1.手动释放Autorelease
2.Runloop结束后自动释放
二十二、苹果为什么要废弃dispatch_get_current_queue?
答: dispatch_get_current_queue容易造成死锁
二十三、KVC的keyPath中的集合运算符如何使用?
答:
1.必须用在集合对象或普通对象的集合属性上
2.简单几何运算符有@avg,@count,@max,@min,@sum。
3.格式@"@sum.age"或@“集合属性.@max.age”
例子:

列表里面所有员工的平均薪酬。通常可能会这样写
double totalSalary = 0.0;
for (Employee *employee in employees) { 
    totalSalary += [employee.salary doubleValue];
}
double averageSalary = totalSalary / [employees count];

KVC提供了一种更加简洁的方法,那就是使用集合运算符

[employees valueForKeyPath:@"@avg.salary"];

二十四、KVC和KVO的keyPath一定是属性吗?
答:KVC支持实例变量
二十五、lldb常用的调试命令?
答:
1.breakpoint 设置断点定位到某一个函数
2.n 断点指针下一步
3.po 打印对象
二十六、类方法load和initialize的区别?
答:参考细碎知识总结一:第15条
二十七、HTTP和HTTPS
答:
1.HTTPS的标准端口443,HTTP标准端口80
2.HTTPS基于传输层,HTTP基于应用层
二十八、常见的Exceptition Type
答:
1.EXC_BAD_ACCESS系列,一般时访问了不该访问的内存地址,比如野指针,插入nil到数组中等。
2.EXC_BAD_INSTRUCTION,此类异常通常由于线程执行非法指令导致。
3.EXC_ARITHMETIC,除零错误会抛出此类异常。
二十九、如何访问并修改一个类的私有属性
答:
1.通过KVC
2.通过runtime访问并修改(object_setIvar方法)
三十、如何为class定一个对外只读、对内可读写的属性
答:在头文件.h中将属性定义为readonly,在.m中将原属性重新定义为readwrite。
三十一、创建一个对象的步骤
答:
1.开辟内存空间
2.初始化参数
3.返回内存地址
三十二、id和instancetype有什么区别?
答:
1.id:万能指针,能作为参数、方法的返回类型
2.instancetype:只能作为方法的返回类型,并返回的类型是当前定义类的类类型。
三十三、内存管理
答:
ARC所做的事在代码编译期自动在合适的位置插入release或autorelease,只要没有强指针指向对象,对象就会被释放。ARC中不能手动使用NSZone,也不能调用父类的delloc。
三十四、APP启动过程,从main说起
答:iOS App 的冷启动(Cold Start)指从用户点击图标到首屏内容渲染完成的全过程,可分为 4 大阶段:、

一、 预处理阶段:系统加载 App 二进制文件,准备运行环境
预处理阶段主要包括:
1.系统通过LaunchServices找到 App 的可执行文件(.app 包内的二进制文件),
2.内核创建进程,分配内存空间,并将 App 二进制文件映射到内存。
3.加载 Info.plist 配置:系统解析Info.plist中的关键配置
3.1) LSRequiresIPhoneOS:确认设备兼容性
3.2) UISupportedInterfaceOrientations:设置初始屏幕方向
3.3) UIApplicationSceneManifest:多窗口场景配置(iOS 13+)
3.4)UIApplicationShortcutItems:3D Touch 快捷方式
二、加载阶段:动态链接器(dyld)处理依赖库和符号
dyld(动态链接器)的核心工作(dyld 流程(深度面试常问)):
1.加载主程序二进制:读取 App 的 Mach-O 文件(包含代码段、数据段等)
2.递归加载依赖库:解析LC_LOAD_DYLIB指令,加载Frameworks和动态库
3.递符号绑定:将符号(函数名、变量名)映射到内存地址
4.Rebase 与 Bind:修正指针地址(ASLR 机制下的地址随机化处理)

三、初始化阶段:运行时(runtime)初始化类、对象及 AppDelegate
初始化阶段:从 main 到 AppDelegate
1 . main 函数前的初始化
1.1)libSystem 初始化:设置进程环境、初始化堆内存等
1.2)objc runtime 初始化:主要包括类的注册(class_registerNonMetaClass)和分类(category_register)、处理load方法
1.3)C++ 全局变量与构造函数:初始化static变量和__attribute
_((constructor))函数
1.4)Swift 初始化:处理@main属性、全局变量及@objc标记的类

2.执行main函数‌:应用启动开始于main函数,该函数负责初始化运行环境。通常,这个函数会调用UIApplicationMain来启动应用程序‌。
UIApplicationMain的核心工作:
2.1)创建UIApplication实例
2.2)加载Info.plist中的Main storyboard(若配置)
2.3)创建UIWindow
2.4)调用AppDelegate的生命周期方法
2.5)给window设置rootController

四、 渲染阶段:构建 UI 层级并完成首屏绘制
主要 包括:1. 窗口与根控制器的加载。2.图层合成与显示(Core Animation )3.首屏渲染完成标志

三十五、断点续传如何实现的
答:断点续传的理解可以分为两部分:一部分是断点,一部分是续传。
断点的由来是在下载过程中,将一个下载文件分成了多个部分,同时进行多个部分一起的下载,当 某个时间点,任务被暂停了,此时下载暂停的位置就是断点了。续传就是当一个未完成的下载任务再次开始时,会从上次的断点继续传送。

使用多线程断点续传下载的时候,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,多个线程并发可以占用服务器端更多资源,从而加快下载速度。

AFN断点续传过程:
核心:利用HTTP请求头的Range实现断点续传
1.指定下载文件地址 URLString
2.获取保存的文件路径 filePath
3.创建 NSURLRequest,本地文件与服务端文件大小比较,若之前下载过 , 则在 HTTP 请求头部加入 Range
4.创建 AFHTTPRequestOperation
5.设置操作输出流 , 保存在第 2 步的文件中
6. 设置下载进度处理 block
7. 设置 success 和 failure 处理 block
8. 启动 operation

三十六、客户端的缓存机制
答:
缓存分为:内存数据缓存,数据库缓存,文件缓存
每次获取数据的时候,先检测内存中有无缓存, 再检测本地有无缓存(数据库/文件), 最终发送网络请求,将服务端返回的数据进行缓存(内存、数据库、文件)
三十七、是否可以把比较耗时的操作放在NSNotificationCenter中
答:发送通知和通知监听方法在同一线程,所以
1.如果在异步线程发的通知,可以执行耗时操作
2.如果在主线程发的通知,则不可以执行耗时操作
三十八、ASIHTTPRequest和CustomAFNetworking区别?
答:

  1. 底层实现方面
    1.AFN的底层基于OC的(NSURLConnection与NSURLSession),ASI的底层基于C语言(CFNetwork框架)
    2.NSURLConnection与NSURLSession两者是对CFNetwork之上的一层封装,因此ASI的运行性能高于AFN

  2. 对服务器返回的数据处理
    1.ASI没有直接提供对服务器数据处理的方式,直接返回的是NSData/NSString
    2.AFN提供了多种对服务器数据处理的方式

  3. 监听请求过程
    1.AFN提供了success和failure两个block来监听请求过程
    2.ASI提供了3套方案,1.成为代理,遵守协议,实现协议中代理方法,2.成为代理,不遵守协议,自定义代理方法 3.block

  4. 网络监控
    推荐使用AFN做网络监控

  5. ASI的其他实用功能
    1.控制信号旁边的圈圈不要在请求工程中转圈
    2.轻松的设置请求之间的依赖,每一个请求都是一个NSOperation对象
    3.可以统一管理所有请求
    4.暂停\恢复\取消所有的请求
    5.监听整个队列中的所有请求的下载进度和上传进度
    三十九、网络请求优化之取消请求
    在一个界面进行多个请求的时候,而有可能用户马上点击了返回按钮,那么如果是使用了AFNetworking的情况,此时ViewController不会马上销毁,需要等到网络请求返回并执行完毕block后才会销毁此ViewController。
    那么会存在2个问题:
    1.网络请求返回的数据没有使用,浪费流量。
    2.ViewController销毁延迟,内存不能及时释放
    主要有以下几点优化:
    1.页面返回的时候,将网络请求取消
    2.同一个请求多次请求时,短时间忽略相同的请求
    3.同一个请求多次请求时,取消之前发出的请求
    4.发送的请求,多次尝试并确保成功
    思路:将页面中进行的所有请求记录,包括controller和view中发起的请求,当然设计为不是强制的,而是通过根据业务选择添加。采用BaseViewController的方式,每一个ViewController都需要继承BaseViewController,然后添加添加请求和取消请求的方法。
    四十、如何检测应用是否卡顿
    答:
    CADisplayLink监控,将CADisplayLink添加到主线程runloop中,一旦屏幕需要刷新时,就会调用CADisplayLink对应的selector方法。
    四十一、OC中对象的结构
    答:
    结构体
    四十二、多态
    答:
    没有继承就没有多态,不同的对象对同一消息的不同响应
    四十三、容错处理你们一般注意哪些?
    答:
    1.字典 2.数组 3.野指针 4.NSNull 5.对服务器返回的数据做容错处理
    四十四、浅谈Swift与OC
    答:
    1.swift中有元组,有泛型
    2.swift定义的常量和变量没有默认值,所以引用了可选值
    3.swift中有各种方便快捷的高阶函数
    四十五、CFSocket使用有哪几个步骤?
    答:
    1.创建socket的上下文
    2.创建socket
    3.配置要访问的服务器信息
    4.封装服务器信息
    5.连接服务器
    四十六、Core Foundation 中提供了哪几种操作Socket的方法?
    答:
    1.CFNetwork
    2.CFSocket
    3.BSD Socket
    四十七、解析XML有哪几种方式?
    答:
    1.以DOM方式解析XML文件
    2.以SAX方式解析XML文件
    四十八、队列和栈有什么区别?
    答:
    队列和栈是两种不同的数据容器,从“数据结构来看”,他们都是线性结构
    队列是先进先出的数据结构,他在两端进行操作,一端进行入队列操作,一端进行出队列操作。
    栈是一种先进后出的数据结构,它只能在栈顶进行操作,入栈和出栈都在栈顶操作。
    四十九、ios的系统结构
    答:分四层
    1.核心操作层(the Core OS layer)
    2.核心服务层 (the Core Services layer)
    3.媒体层 (the Media layer)
    4.界面服务层 (the Cocoa Touch layer)
    五十、控件主要响应3种事件?
    答:
    1.基于触摸的事件
    2.基于值得事件
    3.基于编辑的事件
    五十一、xib文件的构成分为哪三个图标?都有什么功能?
    答:
    1.File‘s Owner 它表示从磁盘加载时nib文件的对象,对应于哪一个class。
    在这里插入图片描述2.First Responder就是用户当前正在与之交互的对象。
    在这里插入图片描述3.view 显示用户界面,完成用户交互
    在这里插入图片描述五十一、Cocoa Touch 提供了哪几种Core Animation过渡类型?

答:4种
分别为交叉淡化、推挤、显示和覆盖
五十二、Quatrz 2D的绘图工功能的三个核心概念是什么?并简述其作用
答:
1.上下文:主要用于描述图形写入哪里
2.路径:是在图层上绘制的内容
3.状态:用户保存配置置换的值、填充、和轮廓、alpha值等。
五十三、iphone OS 主要提供了哪几种播放音频的方法?
答:
一、SystemSound Services
它是最底层的也是最简单的的声音播放服务,调用AudioServicePlaySystemSound这个方法就可以播放一些简单的音频文件,使用此方法只能播放很小的提示或者警告音,因为他有很多限制:
1.声音长度要小于30秒
2.不能控制播放的进度
3.调用方法后立即播放声音
4.没有循环播放和立体声控制
5.打包成.caf .aif 或者.wav的文件
6.In linear PCM 或者IMA4(IMA/ADPCM)格式的
它也可以调用系统的震动功能。
二、AVAudioPlayer类
AVAudioPlayer是AVFoundation.framework中定义的一个类,它支持发广泛的音频格式。AVAudioPlayer可以播放任意长度的音频文件、支持循环播放、可以同步播放多个音频文件、控制播放进度以及从音频文件的任意一点播放。可以对播放的对象进行播放、暂停以及停止操作,在使用AVAudioPlayer连接播放mp3这个类进过压缩的音频文件,在连接处可能出现一定的事件间隔。AVPlayer可以使用NSURL或者NSData来初始化,须注意的是NSURL不可以是网络URL而必须是本地URL(不支持边放边播,只能存储到本地)
三、Audio Queue Service
使用Audio Queue Services对音频进行播放,你可以完全年实现对声音的控制。例如:你可以在声音数据文件读到内存缓存区后对声音进行一定处理再进行播放,从而实现对音频的快速/慢速播放的功能。
四、openAL
openAL是一套跨平台的开源的音频处理接口,与图形处理的OpenGL类似,他为音频播放提供了一套更加优化的方案,他比较适合开发游戏的音效。
五十四、ViewController的didReveiveMemoryWarning怎么被调用?
答:
ios6之后就不再支持viewDidUnload了,不用管这个东西了,如果想要做优化,不要把subviews当成成员变量来持有,使用tag来操作。
五十五、对于OC你认为最大的优点和最大的不足是什么?
答:
最大的优点是它的运行时机制,不足是没有命名空间。
对于命名冲突,可以使用长命名法或特殊前缀解决,如果引入的第三方库之间的命名冲突,可以使用link命令以及flag解决冲突。
补充:在OC中所有类名都必须是全局唯一的。苹果官方建议两个字母作为前缀的类名视为官方的库和框架准备的,而作为第三方开发者,官方建议使用3个或者更多的字母作为前缀去命名我们的类。
五十六、通知和协议的不同之处
答:
协议有控制链(单一拥有和可控制的对应关系)通知没有。
五十七、简单介绍NSURLConnect类及+sendSynchronousRequest:returningResponse:error与-initWithRequest:delegate:两个方法的区别?
答:
NSURLConnection主要用于网络访问,其中+sendSynchronousRequest:returningResponse:error是同步访问数据,即当前线程会阻塞,并等待request的返回response,而-initWithRequest:delegate:使用的是异步加载,当起完成网络访问后,会通过delegate回到主线程。
五十八、EventBus讲一讲?
答:
EventBus是一种发布-订阅时间总线,代码简介,开销很小,并很好地实现了发送者和接受者的解耦。三大要素:A:Event事件,B:Publisher:发布者,C:订阅者
五十九、ipv6和ipv4的区别,CustomAFNetworking是否支持ipv6
答:
1.ipv4地址是32位
2.ipv6地址是128位,地址极为丰富
3.CustomAFNetworking支持ipv6
六十、多线程间的通讯,Binder机制
答:
从IPC角度来说,Binder是Android的一种跨进程通信的方式。
六十一、使用method swizzling要注意什么?
答:
进行版本迭代时要进行一些检验,防止系统库的函数发生变化。
六十二、isEqual和hash的关系
答:
isEqual相等,hash值一定相同。
hash值相等,isEqual不一定相等。
六十三、怎样才能让程序在运行过程中不锁屏呢?
答:
UIApplication单利对象的idleTimerDisabled属性设置为YES。

//阻止屏幕变暗,默认为NO,慎重使用本功能,因为非常耗电
 [UIApplication sharedApplication].idleTimerDisabled = YES;

六十四、平时你访问哪些技术网站?
答:
1.优快云、简书、掘金
2.github、cocoachina
3.一些技术公众号:AI技术前线、infoQ
六十五、block与函数指针的区别?
答:
函数指针仅仅是一个地址,不具备函数原型信息,没有类型限制。
block作为函数对象,是有部分函数信息的,类型限制明确。
六十六、YYModel底层原理
答:
采用了objc_msgSend方法调用了属性的setter方法进行赋值。
六十七、线程死锁
答:
死锁的原因:
1.系统资源不足
2.进程(线程)推进的顺序不恰当
3.资源分配不当
形成死锁的四个条件:
1.互斥条件:进程在某一个时间内独占资源。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
六十八、Python比较重要的几个特征?
答:
python是一门面向对象的动态语言。
1.开源
2.解释性
3.多框架
4.多目的
六十九、UIKit Dynamics 与UIKit Animation的最本质区别是什么?
答:
UIKit Dynamics 是一个集成到UIKit中的完美物理引擎。它允许您通过添加诸如重力等行为来创建感觉真实的界面。
UIKit Animation 进行一些比较简单的动画,比如渐变、旋转、平移、翻转等。
七十、了解fishhook吗?
答:
fishhook
fishhook是facebook提供的一个动态修改链接Mach-O符号表的开源工具。更前线的解读是,如果你的程序调用了动态库中的函数A,可以通过fishhook,在运行时将调用函数A改为调用函数B。
Mach-O
Mach-O为Mach Object文件格式的缩写,也是用于ios可执行文件,目标代码,动态库,内核转储的文件格式。Mach-O有自己的dylib规范。
PIC
PIC技术叫位置代码独立,在Mach-O文件中会预留出一段空间,这一段空间叫符号表,在Mach-O文件的数据段中。dyld在加载Mach-O文件到内存后,会将共享区的系统函数地址绑定到对应的符号上,并插入到符号表中。这样在项目中调用相关函数时,实际上调用的是对应的符号,通过符号来找到具体的系统函数地址。
fishhook原理:
fishhook主要利用共享缓存功能和PIC技术来实现hook功能。也是篡改内存中_la_symbol_Ptr中的内容,把原本应该存函数A地址的那块内容,改为函数B的地址。
七十一、解决Hash冲突的方法
答:
1.开放定址法
2.单独链表法
3.双散列法
4.再散列法
七十二、微信TableView滑动时,动图不动,为什么?
答:
RunLoop的Mode决定
七十三、HTTP常见的返回码?
答:
1xx表示服务器已接收了客户端的请求,客户端可以继续发送请求。
200 表示服务器已经成功接收到请求并进行处理
3xx 表示服务器要求客户端重定向
4xx 网页找不到,请求路径错误
500 服务器发生错误
503 服务器不可用
七十四、ios线程之间如何进行通信?
答:
1.NSThread提供了两个方法实现线程通信

 -void)performSelectorOnMainThread:WithObject: waitUntilDone:
 -void)performSelector:onThread:withObject: waitUntilDone:

2.GCD中get_main_queue
3.NSMachPort(代码稍多)
4.NSTask(越狱开发时会用到)
七十五、TCP的慢开始和快恢复
答:
TCP控制拥塞对的四种算法:慢开始,拥塞避免、快重传,快恢复。
慢开始算法和拥塞避免算法:发送方会维持一个拥塞窗口
快重传:可以提高网络的吞吐量
快恢复:相当于拥塞避免算法的后半恢复部分的优化。
七十六、ios反射机制
答:
反射这个概念在OC中其实是比较模糊的,OC的反射机制可以理解为通过一个字符串,找到对应的class和selector的过程。
用途:
1.获取class对象
2.通过字符串获取方法名
3.常用的判断方法(比如:iskindOfClass)
七十七、OC中所有的类都集成NSObject吗?
答:
OC中有两个NSObject,一个是NSObject类,一个是NSObject协议,并不是所有的类都继承NSObject,比如NSProxy类。
七十八、LRU算法怎样实现?
答:
LRU(least recently used):是将近期不会访问的数据淘汰掉,也叫缓存淘汰算法。
七十九、isa指针里有什么?
答:
isa指针,OC对象都有,实例对象的isa对象指向类对象,类对象的isa指针指向元类对象。
八十、TPC是怎样保证数据完整性的?
答:
主要原因是:TCP有以下几种机制:确认应答、超时重传、流量控制、拥塞窗口。
八十一、http怎样区分header和body?
答:
HTTP协议的header 是一块数据区域,分为请求头和响应头两种类型,请求头里主要是客户端的一些基础信息,响应头里是响应数据的一些信息。
header主要放cookie,token等信息,body主要存放post的一些数据。
八十二、视频的pts和dts?
答:
I帧、B帧、和P帧的区别?
I帧:I帧图像采用帧内编码方式
P帧:P帧图像采用帧间编码方式,但采用的是前向时间预测
B帧:B帧图像采用帧间编码方式,但采用双向时间预测
DTS和PTS的概念:
DTS:即解码时间戳,这个时间戳的意义在于告诉播放器该什么时候解码这一帧数据。
PTS:即显示时间戳,这个时间戳用来告诉播放器该在什么时候显示这一帧的数据。
八十三、通知的发送和接受是否在同一线程?
答:
通知的发送和接受是在同一线程。
如果发送不在主线程,监听收到通知的方法也不在主线程, 如果需要UI操作,要先回到主线程。
八十四、为什么说http是无状态的?
答:
无状态是指这种协议对于事物处理没有记忆功能,每个请求都是独立的。
http这种特性有优点也有缺点,优点在于:解放了服务器,每一次请求都不会造成不必要的连接占用,缺点在于每次请求会传输大量重复的内容信息。
八十五、组件化技术选型考虑(URL调度方案优缺点)、target-action组件化方案的优缺点?
答:
目前市场上流行的组件化方案都是通过url-scheme实现的,包括很多开源的组件化库都是如此,只有case独树一帜,是通过target-action方案实现。
URL-Scheme库:1.JLRoutes 2.routable-ios 3.HHRouter 4.MGJRouter
Target-Action库:1.CTMediator
八十六、静态库中.a与framework的区别?
.a是一个纯二进制文件,.framework中除了有二进制文件之外还有资源文件
.a文件不能直接使用,需要配合对应的.h头文件。.framework文件可以直接使用,它本身包含了.h文件和其他文件。
八十七:nil、Nil、NSNull和NULL区别
nil
nil是用来表示一个对象是空对象,即想要表示此对象不存在。给对象赋值时一般会使用object = nil,表示我想把这个对象释放掉;或者对象引用计数器为0了,系统将这块内存释放掉,这个时候这个对象被置为nil。
Nil
Nil是用来表示一个类是空类。比如:Class myClass = Nil;。和nil没有明确的区分,也就是说凡是使用nil的地方都可以用Nil来代替,反之亦然。约定俗成地将nil表示一个空对象,Nil表示一个空类。
NULL
NULL是在C/C++中的空指针,在C语言中,NULL是无类型的,只是一个宏,它代表空。C语言中,当我们使用完一个指针以后,通常会设置其指向NULL。如果没有设置,这个指针就成了所谓的野指针,然后其它地方不小心访问了这个指针是很容易造成非法访问的,常见的表现就是崩溃了。

既然Objective-C是基于C语言的面向对象语言,那么也会使用到C语言类型的指针,比如使用const char *类型,判断是否为空时,是使用p != NULL来判断的。

NSNull
NSNull是继承于NSObject的类型。它是很特殊的类,它表示是空,什么也不存储,但是它却是对象,只是一个占位对象。
NSNull类中只有一个方法,所以给这个对象发送一个它无法响应的消息自然会发生崩溃,报错:unrecognized selector。

在这里插入图片描述八十八:iOS与JS交互的方法
1.URL拦截(适用于UIWebView和WKWebView)
2.JavaScriptCore(只适用于UIWebView,iOS7+)
3.WKScriptMessageHandler(只适用于WKWebView,iOS8+)
4.WebViewJavascriptBridge(适用于UIWebView和WKWebView,属于第三方框架)
UIWebView 原生的交互原理
通过一个 JSContext 获取 UIWebView 的 JS 执行上下文。
然后通过这个上下文,进行 OC & JS 的双端交互。

 _jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
 _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        NSLog(@"%@",@"获取 WebView JS 执行环境失败了!");
    };

WKWebView 原生交互原理
通过 userContentController 把需要观察的 JS 执行函数注册起来。
然后通过一个协议方法,将所有注册过的 JS 函数执行的参数传递到此协议方法中。
注册 需要 观察的 JS 执行函数

 [webView.configuration.userContentController addScriptMessageHandler:self name:@"jsFunc"];

在 JS 中调用这个函数并传递参数数据
OC 中遵守 WKScriptMessageHandler 协议。

//此协议方法里的 WKScriptMessage 有 name & body 两个属性。 name 可以用来判断是哪个 JSFunc 调用了。body 则是 JSFunc 传递到 OC 的参数。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message 

WebViewJavaScriptBridge
WebViewJavaScriptBridge 用于 WKWebView & UIWebView 中 OC 和 JS 交互。
它的基本原理是:

把 OC 的方法注册到桥梁中,让 JS 去调用。
把 JS 的方法注册在桥梁中,让 OC 去调用。
请添加图片描述WebViewJavaScriptBridge目录结构:
![在这里插入图片描述](https://img-blog.csdnimg.cn/41f76f3d45224c229fc351318672e357.png#pic_center

小结:
一句话来概括的话,那就是——WebViewJavascriptBridge 以下面两个方法为桥梁(以 UIWebView 为例):

  • 原生调 JS:-stringByEvaluatingJavaScriptFromString:
  • JS 调原生:- webView:shouldStartLoadWithRequest:navigationType:

在 JS 和原生两边封装了一套『方法调用』转『消息发送』的机制,各自维护了一套注册的方法列表、回调函数列表,优雅地解决了回调的问题。(注:不要把这里的消息发送和 Objective-C 运行时的消息发送混淆了)

关于在 OC 中,往桥梁中注入 block 的注意点

在当前控制器消失的时候,要记得把注入到桥梁中的 OC block,从桥梁中删除。
否则,可能会出现控制器无法释放的情况。

介绍一下WKWebView的WKWebViewConfiguration
WKWebViewConfiguration‌ 是一个用于初始化 web 视图的配置对象,它包含了一系列属性,用于配置 WKWebView的行为和外观。这个配置对象在创建 WKWebView 时使用,并且一旦创建,其设置不能动态更改‌。
1.AllowsPictureInPictureMediaPlayback‌:这个属性控制是否允许PIP(Picture-in-Picture)播放。通过设置这个属性,可以控制视频内容是否可以在后台播放‌。
2.AllowsBackForwardNavigationGestures‌:这个属性控制是否允许通过手势进行前后导航。例如,通过左滑返回上一页或右滑前进到下一页‌。
3.websiteDataStore:用于管理网站数据存储。
4.preferences‌:用于设置网页偏好。比如:preferences‌.javaScriptCanOpenWindowsAutomatically、preferences‌.minimumFontSize。
5.userContentController‌:用于处理用户内容控制器。
6.mediaPlaybackAllowsAirPlay‌:控制是否允许通过 AirPlay 播放媒体。
7.allowsInlineMediaPlayback‌:控制是否允许内联媒体播放。
8.applicationNameForUserAgent‌:设置用户代理中的应用程序名称。
9.supressesIncrementalRendering‌:控制是否抑制增量渲染。

如何在WKWebView中监听某个DOM元素加载完毕
在WKWebView中监听某个DOM元素加载完成可以使用WKScriptMessageHandler协议。你需要在WKWebView的配置中设置userContentController,然后添加一个自定义的JavaScript函数来在DOM元素加载完成时发送消息。

class ViewController: UIViewController, WKScriptMessageHandler {
 
    var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let config = WKWebViewConfiguration()
        config.userContentController.add(self, name: "domReady")
        
        let scriptSource = """
        function domReady() {
            var element = document.getElementById('yourElementId');
            if (element) {
                window.webkit.messageHandlers.domReady.postMessage(null);
            }
        }
        window.onload = domReady;
        """
        
        let script = WKUserScript(source: scriptSource, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        config.userContentController.addUserScript(script)
        
        webView = WKWebView(frame: view.bounds, configuration: config)
        view.addSubview(webView)
        
        let urlRequest = URLRequest(url: URL(string: "https://yourwebsite.com")!)
        webView.load(urlRequest)
    }
    
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "domReady" {
            print("DOM element is ready")
        }
    }
}

在这个示例中,当WKWebView加载完成后,它会执行你注入的JavaScript函数domReady,该函数会检查是否存在ID为yourElementId的DOM元素,如果存在,就通过postMessage发送一个消息给WKScriptMessageHandler。你可以在userContentController(_:didReceive:)方法中处理这个消息,从而知道DOM元素加载完成。

八十九、Block循环引用三种解决办法

方法一、 对Block内要使用的对象A使用__weak进行修饰,Block对对象A弱引用打破循环。
有三种常用形式:
1.使用__weak ClassName

    __block XXViewController* weakSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",weakSelf);
    };

2.使用__weak typeof(self)

    __weak typeof(self) weakSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",weakSelf);
    };

3.Reactive Cocoa中的@weakify和@strongify

    @weakify(self);
    self.blk = ^{
        @strongify(self);
        NSLog(@"In Block : %@",self);
    };

方法二:对Block内要使用的对象A使用__block进行修饰,并在代码块内,使用完__block变量后将其设为nil,并且该block必须至少执行一次。

    __block XXController *blkSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",blkSelf);
        blkSelf = nil;//不能省略
    };
    
    self.blk();//该block必须执行一次,否则还是内存泄露

方法三、将在Block内要使用到的对象(一般为self对象),以Block参数的形式传入,Block就不会捕获该对象,而将其作为参数使用,其生命周期系统的栈自动管理,不造成内存泄露。

    self.blk = ^(UIViewController *vc) {
        NSLog(@"Use Property:%@", vc.name);
    };
    self.blk(self);

九十、项目中的所有网络拦截

一、原生部分

众所周知,IOS中可以通过继承NSURLProtocol来实现原生网络请求的拦截。
我们不能直接使用NSURLProtocl,它只是个抽象类,需要继承它。
1.实现一个类型继承自NSURLProtocol
2.向系统注册自定义NSURLProtocol

//App启动时注册

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// Override point for customization after application launch.

[NSURLProtocol registerClass:[TestProtocol class]];

return YES;

}
二、在Web网页中拦截网络请求

在WKWebView中是无法通过简单的自定义NSURLProtocol来实现。可以使用JS注入拦截(推荐)。
我们知道,Ajax发起网络请求有几个必要方法:open,send等。我们可以通过重写方法来达到拦截的目的,甚至还可以修改返回参数等。

九十一、多个APP之间的通信

  1. URL Scheme
    App1通过openURL的方法跳转到App2,并且在URL中带上想要的参数。
  2. Keychain
    iOS系统的Keychain是一个安全的存储容器,它本质上就是一个sqllite数据库,它的位置存储在/private/var/Keychains/keychain-2.db。它是独立于每个App的沙盒之外的,所以即使App被删除之后,Keychain里面的信息依然存在。基于安全和独立于app沙盒的两个特性,Keychain主要用于给app保存登录和身份凭证等敏感信息,这样只要用户登录过,即使用户删除了app重新安装也不需要重新登录。那Keychain用于App间通信的一个典型场景也和app的登录相关,就是统一账户登录平台。使用同一个账号平台的多个app,只要其中一个app用户进行了登录,其他app就可以实现自动登录不需要用户多次输入账号和密码。一般开放平台都会提供登录SDK,在这个SDK内部就可以把登录相关的信息都写到keychain中,这样如果多个app都集成了这个SDK,那么就可以实现统一账户登录了。Keychain的使用比较简单,使用iOS系统提供的类KeychainItemWrapper,并通过keychain access groups就可以在应用之间共享keychain中的数据的数据了。
  3. Universal Links
    Universal Links:配置实际可以访问的域名,并且根据配置表,可以将路径和对应的App进行关联。如果已经安装了App可以直接跳转到App。如果没有安装App,可以直接跳转到对应的网页。
  4. UIPasteboard
  5. UIDocumentInteractionController
    UIDocumentInteractionController主要是用来实现同设备上app之间的共享文档,以及文档预览、打印、发邮件和复制等功能。
    6.local socket
    这种方式不太常见,也是很容易被iOS开发者所忽略但是特别实用的一种方法。它的原理很简单,一个App1在本地的端口port1234进行TCP的bind和listen,另外一个App2在同一个端口port1234发起TCP的connect连接,这样就可以建立正常的TCP连接,进行TCP通信了,那么就想传什么数据就可以传什么数据了。
    这种方式最大的特点就是灵活,只要连接保持着,随时都可以传任何相传的数据,而且带宽足够大。它的缺点就是因为iOS系统在任意时刻只有一个app在前台运行,那么就要通信的另外一方具备在后台运行的权限,像导航或者音乐类app。
    它是常用使用场景就是某个App1具有特殊的能力,比如能够跟硬件进行通信,在硬件上处理相关数据。而App2则没有这个能力,但是它能给App1提供相关的数据,这样APP2跟App1建立本地socket连接,传输数据到App1,然后App1在把数据传给硬件进行处理。

九十二、web存储

  1. cookie
    为什么要用Cookie?
    HTTP协议本身是无状态的。什么是无状态呢,即服务器无法判断用户身份。Cookie可以记录访问者的身份。
    通常Cookie是记录身份的作用最简单的使用就是登录功能。
    还有一种使用能够作为全局变量的实现,来跟踪用户行为。
    cookie是浏览器提供的一种机制,它将document 对象的cookie属性提供给JavaScript。可以由JavaScript对其进行控制,而并不是JavaScript本身的性质。cookie是存于用户硬盘的一个文件,这个文件通常对应于一个域名,当浏览器再次访问这个域名时,便使这个cookie可用。因此,cookie可以跨越一个域名下的多个网页,但不能跨越多个域名使用。
    Cookie是有生命周期的,cookie默认的有效期就是从打开浏览器到关闭浏览器的这段时间,关闭了浏览器cookie就过期了;在设置Cookie值时,可以同时设置有效期。当超过了这个有效期之后,Cookie便会失效,前端请求时,不会携带过期的Cookie。
    如果想要某个cookie失效,那么设置这个cookie的expires为过去的某个时间即可。
  2. localStorage
    特点:存储的值是字符串格式,大小一般在5mb左右,能永久性存储;
  3. sessionStorage
    针对一个 session 的数据存储,当用户关闭浏览器窗口后,数据会被删除
  4. Application cache的用法(应用缓存)
    html5便引入了cache manifest 文件,
    什么是应用程序缓存(Application Cache)?
    HTML5 引入了应用程序缓存,这意味着 web 应用可进行缓存,并可在没有因特网连接时进行访问。
    应用程序缓存为应用带来三个优势:
    离线浏览 - 用户可在应用离线时使用它们
    速度 - 已缓存资源加载得更快
    减少服务器负载 - 浏览器将只从服务器下载更新过或更改过的资源。
    Application Cache的使用要做两方面的工作:
    ① 服务器端需要维护一个manifest清单
    ② 浏览器上只需要一个简单的设置即可:

九十三、算法

常见的四种排序算法
使用系统方法排序:

NSArray *array = @[@(12), @(5), @(34), @(2), @(78), @(65), @(13), @(64)];

// 使用block实现快速排序
NSArray *sortedArray = [array sortedArrayUsingComparator:^NSComparisonResult(NSNumber *num1, NSNumber *num2) {
    // 升序排列:如果num1小于num2,返回NSOrderedAscending
    return [num1 compare:num2];
}];

NSLog(@"排序前的数组: %@", array);
NSLog(@"排序后的数组: %@", sortedArray);

// 输出结果:
// 排序前的数组: (12, 5, 34, 2, 78, 65, 13, 64)
// 排序后的数组: (2, 5, 12, 13, 34, 64, 65, 78)

冒泡排序:

//冒泡排序
-(void)bubbleSort {
    NSMutableArray *numbers = [NSMutableArray arrayWithObjects:@"17",@"28",@"36",@"15",@"39", nil];
    NSLog(@"排序前numbers=%@",numbers);
    for (int k = 0; k < numbers.count-1; k++) {
        for (int j = 0; j < numbers.count-k-1; j++) {
            int num1 = [numbers[j] intValue];
            int num2 = [numbers[j+1] intValue];
            //升序排列
            if(num1 > num2){
                [numbers exchangeObjectAtIndex:j withObjectAtIndex:j+1];
            }
            
        }
    }
    NSLog(@"排序后numbers=%@",numbers);
}

选择排序

//选择排序
-(void)selectSort {
    NSMutableArray *numbers = [NSMutableArray arrayWithObjects:@"17",@"28",@"36",@"15",@"39", nil];
    NSLog(@"排序前numbers=%@",numbers);
    for (int k = 0; k < numbers.count-1; k++) {
        for (int j = k+1; j < numbers.count; j++) {
            int num1 = [numbers[k] intValue];
            int num2 = [numbers[j] intValue];
            //升序排列
            if(num1 > num2){
                [numbers exchangeObjectAtIndex:k withObjectAtIndex:j];
            }
        }
    }
    NSLog(@"排序后numbers=%@",numbers);
}

快速排序

/**
 快速排序
 */
- (NSArray *)quickSort:(NSArray *)array {
    if (array.count <= 1) {
        return array; // 基线条件:空数组或只有一个元素的数组已经有序
    }
    
    // 选择基准值(这里选择第一个元素)
    NSNumber *pivot = array[0];
    
    // 分割数组
    NSMutableArray *less = [NSMutableArray array];
    NSMutableArray *greater = [NSMutableArray array];
    
    for (int i = 1; i < array.count; i++) {
        NSNumber *num = array[i];
        if ([num compare:pivot] == NSOrderedAscending) {
            [less addObject:num]; // 小于基准值的元素
        } else {
            [greater addObject:num]; // 大于等于基准值的元素
        }
    }
    
    // 递归排序并合并结果
    NSArray *sortedLess = [self quickSort:less];
    NSArray *sortedGreater = [self quickSort:greater];
    
    return [[sortedLess arrayByAddingObject:pivot] arrayByAddingObjectsFromArray:sortedGreater];
}

插入排序

去掉一个数组中的重复元素

-(void)deleteEqualElementFormArray {
    NSArray *originalArray = @[@"apple", @"banana", @"apple", @"orange", @"banana", @"kiwi"];
    NSSet *uniqueSet = [NSSet setWithArray:originalArray];
    NSArray *uniqueArray = [uniqueSet allObjects];
    NSLog(@"%@", uniqueArray);
}
func deleteEqualElementFormArray() -> Void {
        let originalArray = [1, 2, 3, 2, 4, 5, 3, 6]
        let uniqueArray = Array(Set(originalArray))
        print(uniqueArray) // 输出可能为 [1, 2, 3, 4, 5, 6],顺序可能不同
    }

九十四、图片资源管理

  1. Assets.xcassets

xcode创建工程后默认会有一个Assets.xcassets文件夹,当一组图片放入的时候同时会生成描述文件Contents.json。且在打包后以Assets.car的形式存在。

使用方法:
UIImage *image = [UIImage imageNamed:@"image"];
1.特性:
ImageAssets 也是从图片文件中读取图片数据转为 UIImage, 只不过这些图片数据都打包在 ImageAssets 中,还有一个最大的区别就是图片缓存. 相当于有一个字典, key 是图片名, value是图片对象. 调用imageNamed:方法时候先从这个字典里取, 如果取到就直接返回, 如果取不到再去文件中创建, 然后保存到这个字典后再返回. 由于字典的key和value都是强引用, 所以一旦创建后的图片永不销毁。
2. 优势: 
性能好,节省Disk。Asset Catalogs会用一个高度优化的特殊格式来存所有图片,而不是一个一个的单独的图片资源,会更少的涉及频繁Disk I/O操作,且会按需下载适合你机型的合适分辨率的图片资源; 安全性。图片资源得到一定程度保护(Asset,car不易打开)
当一个 icon 在多个地方需要被显示的时候, 其对应的UIImage对象只会被创建一次, 而且多个地方的 icon 都将会共用一个 UIImage 对象. 减少沙盒的读取操作.
ImageAssets 最主要的使用场景就是 icon 类的图片, 一般 icon 类的图片大小在 3kb 到 20 kb 不等, 都是一些小文件.

2.Resource 方式直:接拖拽到工程

1.使用方式:
将文件直接拖入到工程目录下, 并告诉Xcode打包项目时候把这些图片文件打包进去. 这样在应用的".app"文件夹中就有这些图片. 在项目中, 读取这些图片可以通过以下方式来获取图片文件并封装成UIImge对象:
NSString *path = [NSBundle.mainBundle pathForResource:@"image@2x" type:@"png"];
UIImage *image = [UIImage imageWithContentsOfFile:path];
2.Resource 的特性
在 Resource 的图片管理方式中, 所有的图片创建都是通过读取文件数据得到的, 读取一次文件数据就会产生一次NSData以及产生一个UIImage, 当图片创建好后销毁对应的NSData, 当UIImage的引用计数器变为0的时候自动销毁UIImage. 这样的话就可以保证图片不会长期地存在在内存中.
3.Resource 的常用情景
由于这种方法的特性, 所以 Resource 的方法一般用在图片数据很大, 图片一般不需要多次使用的情况. 比如说引导页背景(图片全屏, 有时候运行APP会显示, 有时候根本就用不到).
4. Resource 的优点

图片的生命周期可以得到管理无疑是 Resource 最大的优点, 当我们需要图片的时候就创建一个, 当我们不需要这个图片的时候就让他销毁. 图片不会长期的保存在内存当中, 所以不会有很多的内存浪费. 同时, 大图一般不会长期使用, 而且大图占用内存一般比小图多了好多倍, 所以在减少大图的内存占用中, Resource 做的非常好.

3.bundle文件:资源包

1.Bundle 文件
简单理解,就是资源文件包。我们将许多图片、XIB、文本文件组织在一起,打包成一个 Bundle 文件。方便在其他项目中引用包内的资源。
2.Bundle 文件是静态的,也就是说,我们包含到包中的资源文件作为一个资源包是不参       加项目编译的。bundle 包中不能包含可执行的文件。它仅仅是作为资源,被解析成为特定的二进制数据。
3.优势:
1.为了方便管理资源文件,可以使用bundle的方式来进行管理
2.使用频率低的可以建立一个bundle存放图片,使用imageWithContentsOfFile加载.

4.沙盒文件目录下

1.使用:
把需要预处理的图片,打包放在zip文件里面,把zip文件放入工程,把图片存在沙盒里面
现从沙盒文件查找->再从bundl查找->最后从主bundle查找
2.使用场景:
沙盒文件下的图片初次一般是使用预处理复制的,之后图片可以从服务器那来进行更新沙盒图片的文件。

Bundle 和 xcassets 的主要区别有:

  1. xcassets 里面的图片,只能通过 imageNamed 加载。Bundle 还可以通过 imageWithContentsOfFile 等方式。
  2. xcassets 里的 2x 和 3x,会根据具体设备分发,不会同时包含。而 Bundle 会都包含。
  3. xcassets 内,可以对图片进行 Slicing,即裁剪和拉伸。Bundle 不支持。
  4. Bundle 内支持多语言,xcassets 不支持。

综上:另外,使用 imageNamed 创建的 UIImage,会立即被加入到 NSCache 中(解码后的 Image Buffer),直到收到内存警告的时候,才会释放不在使用的 UIImage。而 imageWithContentsOfFile。它每次都会重新申请内存,相同图片不会缓存。所以常用的、较小的图,应该放在 xcassets 内管理。而大图应该放在 Bundle 内管理。

九十五、merge和rebase区别

Merge 保留了分支的独立提交历史
Rebase 则将分支的提交移动到其他分支的最新提交之后,使提交历史保持线性。
选择使用哪种方式取决于你的需求和团队的工作流程。

九十六、PDF内容文字搜索、编辑

在iOS开发中,要实现PDF内容的文字搜索,可以使用CGPDFDocumentRef来读取PDF文档,并使用CGPDFScanner来扫描文档内容,在扫描过程中通过设置相应的回调函数来获取文本信息。

在iOS开发中,要实现PDF内容的文字搜索和编辑,可以使用PDFKit框架。

要实现编辑功能,你需要使用PDFPage的addAnnotation方法添加注释,并使用PDFAnnotation来创建文本注释,允许用户编辑文本。

九十七、OC中的Block与Swift中的闭包有什么区别

1.两者语法(写法)区别
2.值的捕获的区别

-(void)testOCBlock {
    NSInteger a = 100;
    void(^block)(void) = ^{
        NSLog(@"block = %ld:", a);
    };

    a += 1;
    NSLog(@"out1 = %ld:", a);
    block();
    NSLog(@"out2 = %ld:", a);
}

打印:
out1 = 101:
block = 100:
out1 = 101:

OC中的block,在 Block 创建时就会捕获变量的值,之后对外部变量的修改不会影响 Block 内的值。若要修改外部变量,需要用__block来修饰。在OC中编译器走到第三行的时候,实际上已经完成了对a的拷贝。

var a = 100
let closure = {
    print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

打印:
out1 = 101
closure = 101
out 2 = 101
var a = 100
let closure = {
    a += 1
     print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

打印:
out 1 = 101
closure = 102
out 2 = 102

Swift 的闭包:对于引用类型的变量,闭包捕获的是变量的引用;而对于值类型的变量,捕获的是变量的副本。可以直接修改捕获的var变量。
Swift里实现和OC一样的值捕获,看如下代码:

var a = 100
let closure = {
    [a] in
    print("closure = \(a)")
}
a += 1
print("out 1 = \(a)")
closure()
print("out 2 = \(a)")

打印
out 1 = 101
closure = 100
out 2 = 101

闭包内部多了一行[a] in 语法为中括号[]里面添加捕获的变量,然后用in 分割上下分。
3.内存管理区别
在Swift中,闭包会自动对捕获的对象进行管理,不需要手动处理循环引用的问题。
Swift的闭包使用强引用,但在捕获列表中使用weak或unowned关键字可以避免循环引用。

class Person {
    var name: String
    lazy var printName: () -> () = {
        [weak self] in
        print(self?.name ?? "Unknown")
    }

    init(name: String) {
        self.name = name
    }
}

var person: Person? = Person(name: "Alice")
person?.printName() // Alice
person = nil

在Objective-C中,Block需要手动处理循环引用的问题,并且需要使用__weak或__unsafe_unretained关键字指定弱引用。

__weak Person *weakPerson = person;
person.printName = ^{
    NSLog(@"%@", weakPerson.name ?: @"Unknown");
};

person = nil;

在这里插入图片描述

九十八、Get请求与Post请求的区别

主要区别:

  1. 请求方式不同:Get请求对应数据库查询操作,Post对应数据库修改操作。GET请求的数据会附在URL之后,Post请求请求的数据大多放在body体中。GET请求的结果通常可以被缓存,以提高响应速度,多次执行相同的GET请求不会对服务器上的资源状态产生影响。POST请求可能会导致服务器状态的改变,因此其结果通常不会被缓存。
  2. 传输的数据量不同:GET方法传输的数据量一般限制在2KB,而Chrome,FireFox浏览器理论上对于URL是没有限制的,它真正的限制取决于操作系统本身;POST方法对于数据大小是无限制的,真正影响到数据大小的是服务器处理程序的能力。
  3. 安全性方面:GET请求由于参数直接暴露在URL中,不适合传输敏感信息。而POST请求通过将参数放在请求体中,相对更加安全。

header的请求头里一般放哪些内容:

  1. Content-Type:制定发送的数据类型,比如:“application/json”、“application/x-www-form-urlencoded”
[AFHTTPSessionManager manager].requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  1. Authorization:通常用于传递认证信息,通常用于API调用。比如:传递token信息。
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
  1. User-Agent:指发起请求的客户端类型,用于服务端的识别。比如传递操作系统相关内容。
request.setValue("MyApp/1.0 (iPhone; iOS 15.0; Scale/3.00)", forHTTPHeaderField: "User-Agent")
  1. Accept:制定客户端能够处理的内容信息。
request.setValue("application/json", forHTTPHeaderField: "Accept")

九十九、ios项目的编译过程

iOS 编译是将源代码转换为可在 iOS 设备或模拟器上运行的二进制应用程序的过程。以下是 iOS 编译的基本概念和流程:
一、编译工具
1.Xcode
2.Clang/LLVM
Clang:C、C++、Objective-C 的编译器前端。
LLVM:底层编译工具链,处理代码优化和生成二进制文件。
二、编译流程
1.源代码 → 中间代码 (IR)
Clang 将 Swift/Objective-C 代码转换为 LLVM 中间表示 (IR)。
2.中间代码优化
LLVM 对 IR 进行优化(如内联函数、循环展开等)。
3.中间代码 → 机器码
LLVM 将优化后的 IR 转换为特定架构(如 arm64)的机器码。
4.链接
将编译后的代码与系统库、第三方框架链接,生成可执行文件。
5.打包
生成 .app 包,包含可执行文件、资源文件(如图片、故事板)和配置文件(如 Info.plist)。

100、ios对ipa包重新加签

在 iOS 开发中,有时需要对已有的 IPA 包进行重新签名(Re-sign),常见于企业分发、测试环境或需要替换证书的场景。以下是重新签名的步骤和方法:
一、准备工作
1.获取 IPA 文件
从 App Store 下载的应用无法直接重签,需使用开发或测试环境导出的 IPA。
2.签名证书
有效的开发者证书(如 Ad Hoc、企业分发证书)。
3.描述文件(Provisioning Profile)
与证书匹配的描述文件,包含应用 ID 和设备列表(Ad Hoc 需要)。
4.工具
**codesign:**macOS 自带的命令行工具。
**unzip/zip:**用于解压和重新打包 IPA。
二、重签步骤

  1. 解压 IPA 文件
unzip YourApp.ipa -d YourApp
cd YourApp
  1. 移除原有签名

删除Payload/YourApp.app/_CodeSignature目录:

rm -rf Payload/YourApp.app/_CodeSignature
  1. 替换描述文件
    将新的描述文件重命名为embedded.mobileprovision,并复制到Payload/YourApp.app/目录:
cp ~/path/to/new.mobileprovision Payload/YourApp.app/embedded.mobileprovision
  1. 更新 Info.plist 中的 Bundle ID(可选)
    如果需要更改应用的 Bundle ID,编辑Payload/YourApp.app/Info.plist:
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier com.yourcompany.newbundleid" Payload/YourApp.app/Info.plist
  1. 重新签名
    使用codesign命令对 App 和 Frameworks 重新签名:
# 签名主应用
codesign -f -s "iPhone Distribution: Your Name (TEAMID)" --entitlements entitlements.plist Payload/YourApp.app

# 签名Frameworks(如果有)
for framework in Payload/YourApp.app/Frameworks/*; do
    codesign -f -s "iPhone Distribution: Your Name (TEAMID)" --entitlements entitlements.plist "$framework"
done
  1. 创建 entitlements.plist(如果没有)
    从描述文件中提取权限:
security cms -D -i Payload/YourApp.app/embedded.mobileprovision > provision.plist
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' provision.plist > entitlements.plist
  1. 重新打包为 IPA
cd ..
zip -qr YourApp_resigned.ipa YourApp/

三、常见问题与解决方案
1.缺少 Frameworks 或插件
问题:Library/Frameworks或PlugIns目录未签名。
解决:递归签名所有动态库和插件:
2.权限不匹配
问题:codesign报错main executable lacks resource fork, Finder information, or similar detritus。
解决:确保 entitlements.plist 与描述文件中的权限一致。
3.应用崩溃或无法启动
原因:二进制文件与新证书不兼容。
解决:确保使用相同开发者账号签名,或获取原始源码重新编译。
四、自动化工具
手动签名复杂应用时容易出错,可以使用以下工具简化流程:
1.iOS App Signer
图形化工具,支持拖拽 IPA 和证书进行重签。
下载:iOS App Signer
2.ldid
命令行工具,支持绕过某些签名验证:

ldid -S Payload/YourApp.app/YourBinary

101、大视频上传与播放

一、大视频上传方案

  1. 视频压缩与预处理
    核心框架:AVFoundation、VideoToolbox
    关键步骤:
    选择合适的压缩参数(分辨率、码率、帧率)
    后台异步处理,避免阻塞主线程
    提供进度反馈
  2. 分块上传(断点续传)
    核心框架:NSURLSession、FileManager
    关键步骤:
    将视频分割为固定大小的块(如 5MB / 块)
    记录已上传块的索引
    实现块的并行上传和失败重试
  3. 后台上传
    核心框架:NSURLSessionConfiguration.background
    关键特性:
    系统管理上传任务,支持断点续传
    应用进入后台仍可继续上传
    通过代理方法接收上传完成通知

二、大视频播放方案

  1. 流媒体播放(HLS/RTSP)
    核心框架:AVPlayer、AVPlayerViewController
    关键步骤:
    支持标准流媒体协议(HLS、RTSP)
    自动适应网络带宽
    支持后台播放
  2. 本地播放优化
    核心框架:AVFoundation
    关键技术:
    硬件解码(利用 GPU 加速)
    视频缓存策略
    内存管理优化
  3. 视频预加载与缓存
    核心框架:AVAssetDownloadURLSession
    关键步骤:
    实现智能预加载(根据观看进度预测)
    磁盘缓存管理(LRU 策略)
    缓存清理机制

三、性能优化与常见问题

1.内存管理
视频解码:使用硬件加速解码(AVAssetReader + VTDecompressionSession)
帧缓存:限制同时缓存的视频帧数量
内存警告处理:收到警告时释放非必要资源
2.网络优化
自适应码率:根据网络状况动态调整视频质量
HTTP/2:利用多路复用和头部压缩
CDN:选择合适的 CDN 节点降低延迟

四、第三方库推荐
视频上传:
Alamofire:强大的网络请求库,支持分块上传
AFNetworking:经典网络库,适合复杂上传需求
视频播放:
ExoPlayer(跨平台):Google 开源的强大播放器
VLCKit:基于 VLC 的跨平台播放器框架
JWPlayer:支持多种格式和 DRM 的商业播放器

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值