面试题-Foundation相关

737 阅读13分钟

1.nil、NIL、NSNULL` 有什么区别?

  • nil、NIL 可以说是等价的,都代表内存中一块空地址.代表空指针
  • NSNULL代表一个空对象

2.如何实现一个线程安全的 NSMutableArray?

  • NSMutableArray是线程不安全的,当有多个线程同时对数组进行操作的时候可能导致崩溃或数据错误
  • 线程锁:使用线程锁对数组读写时进行加锁
  • 派发队列:多用派发队列,少用同步锁中指出:使用“串行同步队列”,将读取操作及写入操作都安排在同一个队列里,即可保证数据同步。
  • 而通过并发队列,结合GCD的栅栏块(barrier)来不仅实现数据同步线程安全,还比串行同步队列方式更高效。

3.如何定义一台 iOS 设备的唯一性?

  • UUID+keyChain存储
  • 获取UUID
[[UIDevice currentDevice] identifierForVendor] UUIDString]

  • 当你升级或者重装软件的时候,UUID会发生变化,这时候我们再去获取UUID时,得到的值和以前的不一样,但其实还是同一台设备
  • keychain:是钥匙串的意思,是苹果公司Mac OS中的密码管理系统。keychain里保存的信息不会因App被删除而丢失,在用户重新安装App后依然有效,数据还在。所以我们可以将UUID存储到keychain里面,需要用到的时候从keychain中取,这样可以保证UUID的唯一性。

4.atomic 修饰的属性是绝对安全的吗?为什么?

  • 不一定安全,所谓的安全只是局限于 Setter、Getter 的访问器方法而言的
  • 方法里加入一些互斥锁,目的就是防止多(条)线程访问同一个内存地址,造成数据错误。
  • 但是不能保证多线程安全,当开启两个异步线程频繁的去对ojb执行+1操作的时候,就不是线程安全的,解决方法是加锁

5.isEqual 和 hash 方法时要注意什么?

  • hash
    • 对关键属性的hash值进行位或运算作为hash值
  • isEqual
    • ==运算符判断是否是同一对象,因为同一对象必然完全相同
    • 判断是否是同一类型,这样不仅可以提高判等的效率, 还可以避免隐式类型转换带来的潜在风险
    • 判断对象是否是nil, 做参数有效性检查
    • 各个属性分别使用默认判等方法进行判断
    • 返回所有属性判等的与结果

6.id 和 instanceType 有什么区别?

  • 相同点
    • instancetype 和 id 都是万能指针,指向对象。
  • 不同点:
    • id 在编译的时候不能判断对象的真实类型,instancetype 在编译的时候可以判断对象的真实类型。
    • id 可以用来定义变量,可以作为返回值类型,可以作为形参类型;instancetype只能作为返回值类型。

7.说一下对 Super 关键字的理解

  • 使用super关键字发送消息会被编译器转化为调用objc_msgSendSuper以及相关函数
id objc_msgSendSuper(struct objc_super *super, SEL op, ...);

  • 这里的super已经不再是我们调用时写的[super init]的super了,这里指代的是struct objc_super结构体指针。

8.@synthesize 和 @dynamic 分别有什么作用?

  • @property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
  • @synthesize 的语义是如果你没有手动实现 setter 方法和 getter方法,那么编译器会自动为你加上这两个方法。
  • @dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。(当然对于 readonly 的属性只需提供 getter 即可)。
  • 假如一个属性被声明为 @dynamic var,然后你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var =someVar,由于缺setter方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。

9.Objective-C中反射机制了解吗?

  • iOS反射机制:运行时选择创建哪个实例,并动态选择调用哪个方法。
// 当前对象是否这个类或其子类的实例
- (BOOL)isKindOfClass:(Class)aClass;
// 当前对象是否是这个类的实例
- (BOOL)isMemberOfClass:(Class)aClass;
// 当前对象是否遵守这个协议
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
// 当前对象是否实现这个方法
- (BOOL)respondsToSelector:(SEL)aSelector;

  • 获取Class的三种方法
// 通过字符串获取class  
Class class = NSClassFromString(@"NSString");  
NSLog(@"class type : %@", class);  

// 直接用class 来创建对象 ,通过对象来获取class 
id str = [[class alloc] init];   
NSLog(@"%@", [str class]);  

// 通过类来获取class  
NSLog(@"%d", class==NSString.class);   

  • 实际应用
    • 根据后台推送过来的数据,进行动态页面跳转,跳转到页面后根据返回到数据执行对应的操作。
  • OC中使用反射优缺点:
  • 优点:
    • 松耦合,类与类之间不需要太多依赖
    • 构建灵活
  • 缺点:
    • 不利于维护。使用反射模糊了程序内部实际发生的事情,隐藏了程序的逻辑。这种绕过源码的方式比直接代码更为复杂,增加了维护成本
    • 性能较差。使用反射匹配字符串间接命中内存比直接命中内存的方式要慢。当然,这个程度取决于使用场景,如果只是作为程序中很少涉及的部分,这个性能上的影响可以忽略不计。但是,如果在性能很关键的应用核心逻辑中使用反射,性能问题就尤其重要了

10. typeof 和 __typeof,typedef 的区别?

  • typeof
    • 是一个一元运算,放在一个运算数之前,运算数可以是任意类型
    • 可以理解为:我们根据typeof()括号里面的变量,自动识别变量类型并返回该类型。
    • typeof 常见运用于Block中,避免循环引用发生的问题。
  • typedef
    • 定义一种类型的别名,而不只是简单的宏替换。
    • typedef 常用于命名(枚举和Block)

11.self + weakSelf + strongSelf 三者如何使用?

__weak __typeof(self)weakSelf = self;    //1

[self.context performBlock:^{      
    [weakSelf doSomething];          //2
     __strong __typeof(weakSelf)strongSelf = weakSelf;  //3
    [strongSelf doAnotherSomething];        
}];

  • 1.使用__weak __typeof是在编译的时候,另外创建一个局部变量weak对象来操作self,引用计数不变。block 会将这个局部变量捕获为自己的属性,访问这个属性,从而达到访问 self的效果,因为他们的内存地址都是一样的。
  • 2.因为weakSelf和self是两个变量,doSomething有可能就直接对self自身引用计数减到0了.所以在[weakSelf doSomething]的时候,你很难控制这里self是否就会被释放了.weakSelf只能看着.
  • 3.__strong __typeof在编译的时候,实际是对weakSelf的强引用.指针连带关系self的引用计数会增加.但是你这个是在block里面,生命周期也只在当前block的作用域.所以,当这个block结束,strongSelf随之也就被释放了.不会影响block外部的self的生命周期.
  • 4.在 Block 内如果需要访问self的方法、变量,建议使用 weakSelf。
  • 5.如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。

12.如何将 Obj-C 代码改变为 C++/C 的代码?

clang -rewrite-objc -fobjc-arc -fobjc-runtime=macosx-10.13 main.m

13.知不知道在哪里下载苹果的源代码?

16.objc_getClass()、object_getClass()、Class 这三个方法用来获取类对象有什么不同?

14.objc_getClass()、object_getClass()、Class 这三个方法用来获取类对象有什么不同?

  • 全部返回 Class 类对象
  • Class 方法
    • Class 方法无论是类对象还是实例对象都可以调用,可以嵌套,返回永远是自身的类对象。
  • object_getClass 方法
    • object_getClass 和 class 同样可以嵌套,但是 object_getClass 得到的是他的 isa 指向的地址。
  • objc_getClass 方法
    • objc_getClass 无法嵌套,因为参数 是 char 类型,效果和 class 相同

15.NSCache优于NSDictionary的几点?

  • NSCache苹果提供的一套缓存机制
    • 主要作用于内存缓存的管理方面;
    • 在没有引入NSCache之前,我们要管理缓存,都是使用的NSMutableDictionary来管理,如:
// 定义下载操作缓存池
@property (nonatomic, strong) NSMutableDictionary *operationCache;
// 定义图片缓存池
@property (nonatomic, strong) NSMutableDictionary *imageCache;


  • 然而 NSMutableDictionary在线程方面来说是不安全,这也是苹果官方文档明确说明了的,而如果使用的是NSCache,那就不会出现这些问题.
  • 相同点:
    • NSCache和NSMutableDictionary功能用法基本是相同的。
  • 区别:
    • NSCache是线程安全的,NSMutableDictionary线程不安全;NSCache线程是安全的,Mutable开发的类一般都是线程不安全的;

    • 当内存不足时NSCache会自动释放内存(所以从缓存中取数据的时候总要判断是否为空);

    • NSCache可以指定缓存的限额,当缓存超出限额自动释放内存缓存限额:

      • 缓存数量

      @property NSUInteger countLimit;

      • 缓存成本

      @property NSUInteger totalCostLimit;

    • 苹果给NSCache封装了更多的方法和属性,比NSMutableDictionary的功能要强大很多;

代码演示
  • 先定义缓存池,并懒加载初始化:
#import "ViewController.h"

@interface ViewController () <NSCacheDelegate>

// 定义缓存池
@property (nonatomic, strong) NSCache *cache;
@end

@implementation ViewController
- (NSCache *)cache {
if (_cache == nil) {
    _cache = [[NSCache alloc] init];
    // 缓存中总共可以存储多少条
    _cache.countLimit = 5;
    // 缓存的数据总量为多少
    _cache.totalCostLimit = 1024 * 5;
}
return _cache;
}

- (void)viewDidLoad {
  [super viewDidLoad];
  // Do any additional setup after loading the view, typically from a nib.

  //添加缓存数据
   for (int i = 0; i < 10; i++) {
    [self.cache setObject:[NSString stringWithFormat:@"hello %d",i] forKey:[NSString stringWithFormat:@"h%d",i]];
    NSLog(@"添加 %@",[NSString stringWithFormat:@"hello %d",i]);
   }

  //输出缓存中的数据
   for (int i = 0; i < 10; i++) {
    NSLog(@"%@",[self.cache objectForKey:[NSString stringWithFormat:@"h%d",i]]);
   }

}


控制台输出结果为:

68747470733a2f2f75706c6f61642d696d616765732e6a69616e7368752e696f2f75706c6f61645f696d616765732f313438333035392d376431336239623831663732313238392e706e673f696d6167654d6f6772322f6175746f2d6f7269656e742f7374726970253743696d61676556696577322f322f772f343535.png

通过输出结果可以看出:
  • 当我们使用NSCache来创建缓存池的时候,我们可以很灵活的设置缓存的限额;

  • 当程序中的个数超过我们的限额的时候,会先移除最先创建的;

  • 如果已经移除了,那么当我们输出缓存中的数据的时候,就只剩下后面创建的数据了;

  • 演示NSCache的代理方法

先设置代理对象:
  • (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //设置NSCache的代理 self.cache.delegate = self; 调用代理方法: 这里我仅用一个方法来演示:
//当缓存被移除的时候执行
         - (void)cache:(NSCache *)cache willEvictObject:(id)obj{
        NSLog(@"缓存移除  %@",obj);
       }

68747470733a2f2f7374617469632e6f736368696e612e6e65742f75706c6f6164732f696d672f3230313630322f31363232313531375f773432722e706e67.png

  • 通过结果可以看出: NSCache的功能要比NSMutableDictionary的功能要强大很多很多;
当遇到内存警告的时候
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    //当收到内存警告,清除内存
    [self.cache removeAllObjects];
    //输出缓存中的数据
    for (int i = 0; i < 10; i++) {
        NSLog(@"%@",[self.cache objectForKey:[NSString stringWithFormat:@"h%d",i]]);
    }
}

控制台输出结果:

68747470733a2f2f7374617469632e6f736368696e612e6e65742f75706c6f6164732f696d672f3230313630322f31363232303331345f505064382e706e67.png

  • 通过结果可以看出: 当收到内存警告之后,清除数据之后,NSCache缓存池中所有的数据都会为空!
当收到内存警告,调用removeAllObjects 之后,无法再次往缓存池中添加数据
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    //当收到内存警告,调用removeAllObjects 之后,无法再次往缓存中添加数据
    [self.cache removeAllObjects];
    //输出缓存中的数据
    for (int i = 0; i < 10; i++) {
        NSLog(@"%@",[self.cache objectForKey:[NSString stringWithFormat:@"h%d",i]]);
    }
}

// 触摸事件, 以便验证添加数据
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.cache removeAllObjects];
    
    //添加缓存数据
    for (int i = 0; i < 10; i++) {
        [self.cache setObject:[NSString stringWithFormat:@"hello %d",i] forKey:[NSString stringWithFormat:@"h%d",i]];
//        NSLog(@"添加 %@",[NSString stringWithFormat:@"hello %d",i]);
    }
    
    //输出缓存中的数据
    for (int i = 0; i < 10; i++) {
        NSLog(@"%@",[self.cache objectForKey:[NSString stringWithFormat:@"h%d",i]]);
    }

}

控制台输出结果为:

68747470733a2f2f7374617469632e6f736368696e612e6e65742f75706c6f6164732f696d672f3230313630322f31363232313931345f664678622e706e67.png

  • 通过输出结果,我们可以看出: 当收到内存警告,而我们又调用removeAllObjects 之后,则无法再次往缓存中添加数据;

16.NSCache,NSDictionary,NSArray的区别

NSArray
  • NSArray作为一个存储对象的有序集合,可能是被使用最多的集合类。
  • 性能特征
    • 在数组的开头和结尾插入/删除元素通常是一个O(1)操作,而随机的插入/删除通常是 O(N)的
  • 有用的方法
    • NSArray的大多数方法使用isEqual:来检查对象间的关系(例如containsObject:)。有一个特别的方法
indexOfObjectIdenticalTo:

  • 用来检查指针相等,如果你确保在同一个集合中搜索,那么这个方法可以很大的提升搜索速度
NSDictionary
  • 一个字典存储任意的对象键值对。 由于历史原因,初始化方法使用相反的对象到值的方法
[NSDictionary dictionaryWithObjectsAndKeys:object, key, nil]

  • 而新的快捷语法则从key开始
@{key : value, ...}

  • NSDictionary中的键是被拷贝的并且需要是恒定的。如果在一个键在被用于在字典中放入一个值后被改变,那么这个值可能就会变得无法获取了。一个有趣的细节,在NSDictionary中键是被拷贝的,而在使用一个toll-free桥接的CFDictionary时却只被retain。CoreFoundation类没有通用对象的拷贝方法,因此这时拷贝是不可能的(*)。这只适用于使用CFDictionarySetValue()的时候。如果通过setObject:forKey使用toll-free桥接的CFDictionary,苹果增加了额外处理逻辑来使键被拷贝。反过来这个结论则不成立 — 转换为CFDictionary的NSDictionary对象,对其使用CFDictionarySetValue()方法会调用回setObject:forKey并拷贝键。
NSCache
  • NSCache是一个非常奇怪的集合。在iOS 4/Snow Leopard中加入,默认为可变并且线程安全的。这使它很适合缓存那些创建起来代价高昂的对象。它自动对内存警告做出反应并基于可设置的成本清理自己。与NSDictionary相比,键是被retain而不是被拷贝的。
  • NSCache的回收方法是不确定的,NSCache可以设置撑自动回收实现了NSDiscardableContent协议的对象。实现该属性的一个比较流行的类是同时间加入的NSPurgeableData,但是在OS X 10.9之前,是非线程安全的
  • NSCache性能:
    • 那么NSCache如何承受NSMutableDictionary的考验?加入的线程安全必然会带来一些消耗;
iOS 构建缓存时选 NSCache 而非NSDictionary
  • 当系统资源将要耗尽时,NSCache可以自动删减缓存。如果采用普通的字典,那么就要自己编写挂钩,在系统通知时手动删减缓存,NSCache会先行删减时间最久为被使用的对象
  • NSCache 并不会拷贝键,而是会保留它。此行为用NSDictionary也可以实现,但是需要编写比较复杂的代码。NSCache对象不拷贝键的原因在于,很多时候键都是不支持拷贝操作的对象来充当的。因此NSCache对象不会自动拷贝键,所以在键不支持拷贝操作的情况下,该类比字典用起来更方便
  • NScache是线程安全的,NSDictionary不是。在开发者自己不编写加锁代码的前提下,多个线程可以同时访问NSCache。对缓存来说,线程安全通常是很重要的,因为开发者可能在某个线程中读取数据,此时如果发现缓存里找不着指定的键,那么就要下载该键对应的数据了

17.delegate(代理,委托),Notification(通知),KVO分别是什么?

delegate(代理,委托)
  • 委托是协议的一种,顾名思义,就是委托他人帮自己去做什么事。
  • 非常严格的语法。所有将听到的事件必须是在delegate协议中有清晰的定义,语法清晰,易读;
  • 如果delegate中的一个方法没有实现那么就会出现编译警告/错误
  • 在释放代理对象时,需要小心的将delegate改为nil。一旦设定失败,那么调用释放对象的方法将会出现内存crash
Notification(通知)
  • 在iOS应用开发中有一个Notification Center(通知中心)的概念。它是一个单例对象,允许当事件发生时通知一些对象
  • 对于一个发出的通知,多个对象能够做出反应,即1对多的方式实现简单
  • controller能够传递context对象(dictionary),context对象携带了关于发送通知的自定义的信息
  • 在调试的时候应用的工作以及控制过程难跟踪;
KVO
  • KVO提供一种机制,当指定的被观察的对像的属性被修改后,KVO会自动通知响应的观察者,KVC(键值编码)是KVO的基础,KVO基于 runtime
  • 够提供一种简单的方法实现两个对象间的同步。例如:model和view之间同步;
  • 能够对非我们创建的对象,即内部对象的状态改变作出响应,而且不需要改变内部对象(SKD对象)的实现;

18.如何选择delegate、notification、KVO?

三种模式都是一个对象传递事件给另外一个对象,并且不要他们有耦合。
  • delegate. 一对一
  • notification 一对多,多对多
  • KVO 一对一
三者各有自己的特点:
  • delegate 语法简洁,方便阅读,易于调试
  • notification 灵活多变,可以跨越多个类之间进行使用
  • KVO 实现属性监听,实现model和view同步
  • 可以根据实际开发遇到的场景来使用不同的方式

19. 若你去设计一个通知中心,你会怎样设计?

个人理解: 参考现有的通知中心
  • 1.创建通知中心单例类,并在里面有个一个保存通知的全局NSDiction
  • 2.对于注册通知的类,将其注册通知名作为key, 执行的方法和类,以及一些参数做为一个数组为值
  • 3.发送通知可以调用通知中心,通过字典key(通知名),找到对应的 类和方法进行执行调用传值.

20.结构体与数组有什么区别?

  • 结构体可以存不同类型的元素,而数组只能存同一类型
  • 结构体类型需要我们自已定义.数组是用别的类型加[元素个数]
  • 结构体内存分配方式很特别,使用对齐原则,不一定是所有元素的字节数和,而数组一定是所有元素的字节数和.
  • 结构体指针可以指针名->结构体元素名(取元素);数组不行.

21.NSDictionary的实现原理是什么

  • 哈希表(NSDictionary 是通过hash表来实现key和value之间的映射和存储的)

22.OC与 JS交互方式有哪些?

  • 通过拦截URL
  • 使用MessageHandler(WKWebView)
  • JavaScriptCore (UIWebView)
  • 使用三方库WebViewJavascriptBridge,可提供JS调OC,以及OC调用JS

23.JS调用OC代码的方式有哪些?

方法一:拦截url
  • 通过拦截url(适用于UIWebView和WKWebView)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *url = request.URL.absoluteString;
    if ([url rangeOfString:@"需要跳转源生界面的URL判断"].location != NSNotFound) {
        //跳转原生界面
        return NO;
    }
    return YES;
}

方法二:messageHander
  • 当JS端想传一些数据给iOS.那它们会调用下方方法来发送.
  • window.webkit.messageHandlers.<方法名>.postMessage(<数据>)上方代码在JS端写会报错,导致网页后面业务不执行.可使用try-catch执行.
  • 那么在OC中的处理方法如下.它是WKScriptMessageHandler的代理方法.name和上方JS中的方法名相对应.
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

方式三:WebViewJavascriptBridge
1. 设置 webViewBridge
_bridge = [WKWebViewJavascriptBridge bridgeForWebView:self.webView];
    [_bridge setWebViewDelegate:self];
2. 注册handler方法,需要和 前段协商好 方法名字,是供 JS调用Native 使用的。
    [_bridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        // OC调用
        NSString *scanResult = @"http://www.baidu.com";
        // js 回调传参
        responseCallback(scanResult);
    }];
3. OC掉用JS
  [_bridge callHandler:@"testJSFunction" data:@"一个字符串" responseCallback:^(id responseData) {
        NSLog(@"调用完JS后的回调:%@",responseData);
    }];

24.OC调用JS代码方式有哪些?

方式一:stringByEvaluatingJavaScriptFromString直接调用
// 直接运行 使用 
NSString *jsStr = @"执行的JS代码";
[webView stringByEvaluatingJavaScriptFromString:jsStr];
方式二:用JavaScriptCore框架
#import <JavaScriptCore/JavaScriptCore.h>  
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    //获取webview中的JS内容
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    NSString *runJS = @"执行的JS代码";
    //准备执行的JS代码
    [context evaluateScript:runJS];
}

25.Block和Protocol的区别,Block是为了解决什么问题而使用的

  • 代理和Block的共同特性是回调机制,不同的是,代理的方法比较多,比较分散,公共接口,方法较多也选择用delegate进行解耦
  • 使用Block的代码比较集中统一,异步和简单的回调用Block更好 Block是为了解决什么问题而使用的? 个人认为:
  • Block为了多线程之间调度产生的;
  • Block 也是一个OC对象,可以当参数传递,使用方便简单,灵活,很少的代码就可以实现代码回调.比Protocol省很多代码

26.字典的工作原理 ?怎么从100w个中是怎么快速去取value?

  • NSDictionary(字典)是使用 hash表来实现key和value之间的映射和存储的,hash函数设计的好坏影响着数据的查找访问效率。
(void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;
  • Objective-C 中的字典 NSDictionary 底层其实是一个哈希表,实际上绝大多数语言中字典都通过哈希表实现
哈希表:
  • 哈希表的本质是一个数组,数组中每一个元素称为一个箱子(bin),箱子中存放的是键值对。数组长度即箱子数。
  • 哈希表还有一个重要的属性: 负载因子(load factor),它用来衡量哈希表的 空/满 程度,一定程度上也可以体现查询的效率,计算公式为:负载因子 = 总键值对数 / 箱子个数 负载因子越大,意味着哈希表越满,越容易导致冲突,性能也就越低。因此,一般来说,当负载因子大于某个常数(可能是 1,或者 0.75 等)时,哈希表将自动扩容。
  • 参考: www.jianshu.com/p/88dfc8f40…

27. isEquel,hash,isEquelToString 方法的区别?

isEquel
  • isEquel 用于比较2个对象是否相等, 与==(地址比较)不一样, * * isEquel 首先比较2个对象地址,如果相同就返回YES,如果不同,就比较对象类型,以及属性的值,一般重写 isEquel 来比较2个对象
  • isEquel 比较的是2个NSObject的方法
hash
  • hash 是一个类方法,任何类都可以调用这个方法,返回的结果是一个NSInteger值(如果两个对象相等,那么他们的hash值一定相等,但是,如果两个对象的哈希值相等是不能一定推出来这两个对象是相等的)
isEquelToString
  • isEquelToString是比较2个字符串值是否相等

28.iOS 9 以后通知不再需要手动移除

  • iOS 9通知中心持有的是注册者的 unsafe_unretained 指针
  • iOS 9 以后,通知中心持有的是注册者的 weak 指针

29.如何对 NSMutableArray 进行 KVO

  • 一般情况下只有通过调用 set 方法对值进行改变才会触发 KVO。但是在调用NSMutableArray的 addObject或removeObject 系列方法时,并不会触发它的 set 方法。所以为了实现NSMutableArray的 KVO,官方为我们提供了如下方法
@property (nonatomic, strong) NSMutableArray *arr;

//添加元素操作
[[self mutableArrayValueForKey:@"arr"] addObject:item];
//移除元素操作
[[self mutableArrayValueForKey:@"arr"] removeObjectAtIndex:0];

30.函数指针和 Block区别

相同点:
  • 二者都可以看成是一个代码片段。
  • 函数指针类型和 Block 类型都可以作为变量和函数参数的类型
不同点:
  • 函数指针只能指向预先定义好的函数代码块,函数地址是在编译链接时就已经确定好的。从内存的角度看,函数指针只不过是指向代码区的一段可执行代码
  • block 本质是 OC对象,是 NSObject的子类,是程序运行过程中在栈内存动态创建的对象,可以向其发送copy消息将block对象拷贝到堆内存,以延长其生命周期。
声明
本文面试题答案是参考各路大神收集得来,答案不一定是最全面,最合适的,如果哪里有问题,欢迎大家积极讨论。
参考文章:

www.jianshu.com/p/ca1ff3749… github.com/ReginaVicky…