2024 iOS 面试题

一、说一下对自动释放池的理解

ARC中,主线程默认开启一个runloop,runloop自动创建一个autoreleasepool,autorelease对象会自动被加入autoreleasepool中,一次runloop后清空自动释放池,用__autoreleasing修饰符修饰,或类方法创建会自动加入autoreleasepool中,添加到最近的autoreleasepool中。autoreleasepool是被一个栈区管理,当需要释放一个autoreleasepool时,会根据其中的哨兵对象(一个nil指针),对这个对象所在的autoreleasepool中所有对象引用计数-1,清空所有对象的内存地址,然后next指针会指向一个合适的位置,next根据表示begin()和end()来约束autoreleasepool是否存满对象,是否需要创建新的autoreleasepool存储新的autorelease对象。

二、说一下都有那些修饰词并阐述作用

nonatomic、atomic、assign、weak、retain、strong、copy、readonly、readwrite

nonatomic、atomic区别:

atomic:设置成员变量的@property属性时,默认是atomic,提供多线程安全,但不是绝对安全。

nonatomic:禁止多线程,变量保护,提高安全性。

atomic是OC使用的一种线程保护技术,防止在一个线程上的任务未完成时,被其他线程读取,造成数据错误,这种机制是耗费资源的。一般不需要多线程间通讯编程,可以使用nonatomic。

Assign、 weak的区别

引用计数都不会➕1。

assign 修饰基本数据类型,例如int float,在MRC环境下不会自动置为nil。

weak 修饰的变量会在栈中自动清空,赋值为nil。

eg:delegate在MRC环境下,用assign修饰,是为了不造成循环引用,需要在-dealloc方法中手动设置self.delegate=nil;以免造成野指针。而在ARC环境下,直接用weak修饰就可以了。

原理:一个对象,所有指向它的weak指针都存放在一个hash表中,key是对象所占内存地址,value是一个weak指针数组,当对象被销毁的时候,系统会遍历这个数组中的所有weak指针,并将它们都会指向nil。

retain、strong、copy的区别

retain 是MRC下使用的修饰词,在ARC下可用strong替代。

当修饰不可变对象时,三者的作用是一样的,对象不会跟着原本的引用对象的改变而改变,因为是浅拷贝的指针,当源数值发生改变,就会产生新的指针地址。

当修饰可变对象的时候,retain和strong是一样的,对象会跟着原本引用对象的数值改变而改变,因为原对象是可变的,不会出现新的内存。而copy的对象不会跟着改变,因为copy是深拷贝的新的内容,开辟了新的存储空间和指针。

readonly、readwrite的区别

readonly 这个属性只生成了getter方法,没有setter方法,只能读。

readwrite 这个属性getter setter方法都有,可读可改。

三、ARC和MRC的区别

ARC下,内存交给系统自动管理,系统会在编译的时候自动插入retain/release,而MRC下,需要手动管理对象的引用计数,需要手动发送release或者autorelease消息。

四、简述category和extension的区别

category 运行时决定,只能添加方法,不能添加实例变量(可以添加属性)。

extension 编译时决定,不仅可以添加方法,还可以添加实例变量和属性,默认是私有的。但是extension没有独立的实现部分。

category细聊:

1)分类的方法名如果和原类中方法名冲突,优先分类。

2)Category的方法可以被子类继承。

3)利用runtime倒序遍历方法列表,可以调用被覆盖的原类的方法。

4)一个类的两个category,如果存在方法名相同的情况,根据buildPhases->Compile Sources里面的从上至下顺序编译情况去调用,即后编译的会被直接调用。

5)扩展一个类的方式,category比继承要好,继承需要定义子类,类目不需要定义子类来增加现有类的方法,且不会影响其他类和原有类的关系。

五、说一下对内存管理的理解

内存管理机制

1、引用计数:当我们创建一个对象时,其引用计数会➕1,系统会分配内存给到这个对象。每次引用这个对象,它的引用计数都会➕1。当指针不再指向这个对象,那么它的引用计数会➖1,直到引用计数为0,它的内存会释放掉,对象被销毁。在ARC环境下,对象会被系统释放,不需要手动释放,避免了内存泄漏和野指针的出现。

2、循环引用:当几个对象互相强引用时,会造成内存泄漏,导致程序崩溃。例如,delegate如果用strong修饰,那么就会造成两个类之间的互相强引用,此时引入弱引用概念,即用weak修饰delegate,不会使delegate的引用计数➕1,当delegate对象销毁时,指针会指向nil。

3、内存泄漏:没有及时释放掉内存,例如计时器没有释放。

实际开发中如何避免内存泄漏

1、防止循环引用,使用weak弱引用,例如修饰delegate。

2、及时释放对象内存,例如创建计时器NSTimer,在计时结束的时候,一定要调用invalidate方法,将timer置为nil。

3、使用懒加载的方式创建对象,在使用对象的时候再进行实例化,还可以同时加入一些处理逻辑,这样利于内存管理。

六、循环引用

举例:

1、UIViewController中创建UITableView,当实例一个tableView时,控制器的view对tableView进行了强引用,此时tableView的delegate又引用了控制器,这就形成了循环引用,此时需要用weak去修饰delegate。

2、block内部调用了外部变量(控制器本身或者控制器的属性)

此时需要用__weak typeof(self) weakSelf = self;来进行对控制器的弱引用,然后在block内部去调用。

3、关于定时器

使用定时器时,要主动调用invalidated,一旦和控制器形成循环引用,控制器不会被自动销毁,不会调用dealloc方法,如果invalidated放在dealloc中,是不会被执行的。那么我们可以通过拦截点击返回按钮的方法,在该方法中主动调用invalidated方法,或者写在ViewWillDisappear方法中。请记住,创建一个NSTimer对象,会形成一个runloop,不止控制器会对该timer持有,iOS系统也会对其强引用,所以不要以为设置timer=nil就可以释放timer了,只有调用invalidated才会让iOS系统释放对timer的强引用。所以,[timer invalidated];timer=nil;要同时书写,这是一种良好的代码习惯和规范。

七、关于block

1、block是一个OC的对象,它封装了一段代码,这段代码可以在任何时候执行。Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。可以嵌套定义,可以定义在方法内部和外部。

2、block的循环引用:在使用block的地方,self.block,self持有了block,在block代码块里处理逻辑,如果用到了self.xx,那么block又会持有self,这样当你离开界面的时候,并不会走self的dealloc析构函数,self不会释放,这是因为self和block的循环引用,导致了内存泄漏。为了避免这种情况,要使用__weak 在block外面修饰一下self,在block代码块里使用weakself处理逻辑,weakself只是指向了self,它在一个弱引用表里,self的引用计数没有+1。

3、按照上面的方式调用self的属性,有可能会失败,原因是,weakself会因为析构函数被释放,那么weakself.xx就会是nil。为了避免这种情况,在block代码块里,需要用__strong修饰一下weakself,那么相当于一个临时的strong指针指向了self,当逻辑处理完毕,strong销毁,不会产生内存泄漏问题。

八、关于WKWebview

1、相较于UIWebview,WKWebview占内存小,加载快,增加了加载进度条。

2、出现白屏的原因和解决办法:
① 网页内存过大会白屏,需要在webViewWebContentProcessDidTerminate方法中调用[webview reload];方法。
② url中有中文字符,需要后台配合修改;
③ h5中使用了第三方组件,加载失败,先是白屏。一般正式环境下会恢复正常。

3、关于cookie
为了解决加载H5界面无法携带原生界面登录用户信息问题,除了在url上拼接用户uid等信息的方法,还可以在请求的时候,拦截请求头,将这些信息拼接起来,使用request的set value方法,将这些信息传递过去。

4、原生界面和H5界面的交互:
JS调用OC方法:WKWebview中有一个WKScriptMessageHandler协议,想要实现交互,需要定义一个方法名,使用[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"methodname"]方法设置,并通过userContentController:didReceiveScriptMessage方法,获取对应名称的数据体进行解析。
OC调用JS:通过evaluateJavaScript方法调用JS方法,需要在HTML文件中定义方法名,在原生这里调用。

5、获取网页高度
① 在webviewdidFinishNavigation中获取高度。
② 执行evaluateJavaScript方法获取网页宽高,计算比例,再根据手机界面设置的宽高去设置。
③ 在HTML标签中加入特殊字符去接收高度,OC这边通过evaluateJavaScript方法获取。

九、关于多线程

1、关于多线程都有哪些了解?
NSThread NSOperation GCD 当我们需要大量耗时操作时候,可以在多线程中去执行任务,避免主线程阻塞。

2、NSThread使用:
两种方法:
① 创建实例对象:
NSThread*thread = [[NSThread alloc] initWithTarget:self selector:@selector(upload)  object:@"参数"];
[thread start];
② 类方法创建:
[NSThread detachNewThreadSelector:@selector(upload) toTarget:@"" withObject:@""];
③ NSThread可以设置优先级  取值范围 0.0 ~ 1.0 之间 最高是1.0 默认优先级是0.5
举例:thread.threadPriority = 1.0;
④ 强制结束任务调用exit方法。

3、CGD使用
CGD会把任务从队列中取出,自动放到对应的子线程中执行,不需要程序员去管理线程,遵循FIFO原则,先进先出,后进后出。
如何使用:
① 创建队列
//创建一个并行队列   
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);   
//创建一个串行队列   
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
//获取全局并发队列   
dispatch_queue_t queue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   
//获取主队列(一个串行队列,放在主队列的任务都会在主线程执行)   
dispatch_queue_t queue4 = dispatch_get_main_queue();
② 执行任务
dispatch_async(queue, ^{      异步操作      });
dispatch_sync(queue, ^{        同步操作      });
总结:
同一串行队列中,同步+同步 死锁。同步+异步 正常。异步+同步 死锁。异步+异步 正常。
同一并行队列中,都正常。
例如,需要请求1结束后再执行请求2,需要用到dispatch_group_t。
① enter leave方法

enter leave

② 信号量方法

信号量方法

4、NSOperation的使用
基于GCD的封装,更加面向对象。如果是简单的两个网络请求,请求A优先级大于请求B,就可以用NSOperation。使用addDependency方法,来设置先后顺序。如果同时下载多个图片,可以使用NSOperation,设置maxConcurrentOperationCount最大并发数,然后使用NSBlockOperation创建对象去执行任务。

5、其他应用:
① 调用NSObject方法,[self perform.....]
② 延时处理 dispatch_after
③ 使用定时器
④ 创建单例,代码只会执行一次 dispatch_once_t onceToken;
⑤ 栅栏函数,前面的任务执行完毕后才会执行后面的任务。
dispatch_barrier_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

十、手势传递

1、当子view大小超出了父view,怎么获取并响应点击事件?
重新父view的hintTest方法,遍历父view上所有的子view,判断这个point是否在子view上,如果在,返回子view去响应事件,如果都没有,返回父view去响应事件。

示例代码

2、扩大UIButton的点击范围
① 创建分类(Category)并重写UIButton的pointInside:withEvent:方法

代码示例

②创建分类(Category)并重写UIButton的hitTest::withEvent:方法

代码示例

十一、离屏渲染

设置了layer会有离屏渲染

①、②、③、④、⑤、⑥、⑦、⑧、⑨、⑩ ⑪、⑫、⑬、⑭、⑮、⑯、⑰、⑱、⑲、⑳

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容