iOS笔记

1.对于 NSString 这个类,判断两个实例相等时,==、isEqual 方法和 isEqualToString 方法有什么区别?我们平常用的时候应该用哪一个呢?

==判断的仅仅只是指针,而一般isEqual方法判断的是内容,对于NSString来说,isEqual根isEqualToString的区别在于.前者还会判断是不是同一个类,如果你能确定是两个string相互比较,调用isEqualToString的效率要高一点,在isEqualToString: 里面会判断传入参数的类型的;

2.假设有一个 NSString* foo,描述一下调用 [foo isKindOf:NSArray.class] 的时候,objc_msgSend 都做了哪些事情吧~~
 NSString *foo;
((BOOL (*)(id, SEL, Class))(void *)objc_msgSend)((id)foo, sel_registerName("isKindOfClass:"), ((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("class")));
复制代码

objc_msgSend 在寻找方法的时候,总体的流程是先找这个类。在这个例子里,就是先找 NSString (或者更具体的、我们看不到的实现类比如 CFString 什么的)这个类的方法列表,看看有没有这个方法;如果没有这个方法,就找它的父类…… 这样一层一层往上找,直到 NSObject,就找到了这个方法。当然如果找到最上层,还找不到方法,还有 message forward 的处理. 每个类会对上面这个过程的结果有一个缓存。具体实现是类似一张哈希表,以 selector 为键,找到的 imp 为值。所以,第一步其实是到这个类的缓存里找,如果缓存命中,就不用重复上面那个一层一层往父类找的过程了,如果没找到,再进行上面寻找方法的流程

更多的介绍可以看下这篇文章,作者Mike Ash

3.加载图片的方式:

1.imageNamed:有缓存

2.imageWithContentsOfFile:无缓存

注意:加载Assets.xcassets里面的图片:

打包后变成Assets.car,拿不到路径,只能通过imageNamed:的方式来加载图片,不能通过imageWithContentsOfFile方式来加载图片

4. UISearchBar的一些设置

1.设置searBar输入框的背景色

[[[self.searchBar.subviews objectAtIndex:0].subviews objectAtIndex:1] setBackgroundColor:[UIColor greenColor]];
复制代码

2.改变searchBar中占位字的字体颜色

UITextField *searchField = [self.searchBar valueForKey:@"_searchField"];
[searchField setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];
复制代码
5.写过 java 的同学都知道,java 充满了 try catch,做很多事情都要用 try 包裹。OC 里也有 try catch 和 exception 结构,但我们工程中用得却不多,这是为什么呢? 另外,swift 呢?

在OC里面的异常跟错误两种。首先肯定的是try catch可以捕获应用异常,而OC一旦发生异常就会崩溃,那么try catch机制看起来可以帮助我们减少应用的崩溃,但是我们看看OC里常见的异常有哪些: 1、越界 2、消息发送错误 3、zombie

1、3情况其实可以归属于一种大类型:内存使用错误。既然内存被错误使用了,那么为了避免发生更严重的后果,我们应该直接让程序崩溃而不是尝试去catch它。因为一旦这个操作被catch了应用继续运行的时候可能会访问到私密的内存数据,从而导致更严重的后果, 2情况在消息发送错误的情况下,系统本身提供了消息转发的阶段让我们可以避免应用crash,而且通过转发还能实现多种功能,因此catch这种异常并没有什么意义

另外就要提到OC的try-catch机制,本身作为C语言的扩展语言,异常捕获机制基于C标准库的setjmp跟longjmp函数实现,在尝试捕获一块代码的异常的时候,try-catch做了大量的工作,具体可以参考 http://www.cnblogs.com/markhy/p/3169035.html 这篇文章,相比起catch异常的回报,消耗太大,所以OC不推荐使用try-catch,除了异常之后,应用故障还包括Error类型。Error类型就有趣的多,发生exception的时候基本而言应用是遇到了无法继续运行下去的事件所触发的。但是Error是针对某种特定操作过程中可能会发生错误,但是错误不致命,比如把NSData跟NSDictionary互转的过程中可能会导致Error,但是后果也只是两者其中一者为空,并不影响整个应用的安全性。又比如网络请求可能会发生Error。简单来说iOS的异常是致命的,但是还有Error作为操作异常的补充,所以try-catch没有足够的使用条件,就是抛出exception之后,方法里剩余代码本应release的对象不会再release,会造成内存泄漏。如果大量使用try catch,内存泄漏会越来越严重

6.在 OC 开发中常用哪些方式来报告有错误呢,比如说,数据解析失败,网络请求失败,链接中断……等等

常用以下几种方法:

  1. 异常返回值:如方法返回值为 BOOL,成功返回 YES,失败返回 NO。或数学计算,出错时返回 NAN (not a number)
  2. 用参数返回 error。传入一个 error 地址,如果有错误就把错误通过那个地址传出
  3. delegate 方法,比如我们熟知的网络请求,失败时会调用一些 didFail 方法
  4. 有 delegate 就会有 block,也是很多网络框架常用的,参数传入 success、failure block,成功调 success,失败调 failure

我觉得 NSAssert 不算是一种错误处理,它主要是调试时候用的,只能描述现阶段对条件的要求,不是用来让调用者知道有错误了,该处理错误了。java 里不太一样,可以用 assert 抛出异常,OC 里一般不这么用。

7.怎么把 error 用参数传出去。两点:1. 基本用法是 *error = [NSError errorWithDomain…] 2. 不要在 block 中产生这个 error,3.这个 testWithEnumBlockError 除了在 block 中产生 error 之外还有一个问题~~ 做实验的时候这么写无所谓,但在实际工程中一定要避免 >< 是什么呢?~
  • 就是用参数返回 error 的时候,在写 *error = [NSError errorWithDomain…] 前 一定要判断一下 if(error != nil) 不然就会 crash 啦

因为调用者可能不关心这个 error ,所以传进来的就是 nil,nil 就是 0,修改地址为 0 处的值肯定不让改的 ><

8.问题是,代理协议里经常会有一些 @optional 的方法,delegate 有可能实现、也可能不实现这个方法。比如 UIScrollView,如果我们关心它的滚动事件,就写一个 didScroll 方法,不关心就不写。那么,如果让你来写一个 UIScrollView,你怎么知道该不该调用 delegate 的 didScroll 方法呢?~在不知道 delegate 实现没实现 didScroll 方法时,如果我知道我滚了,该怎么调用 delegate 方法呢?比如我直接调 [self.delegate didScroll…] 可能会 crash 是吧:
  • 最简单的方法是判断 if (self.delegate respondToSelector:…) 如果 respond 再调,如果这个方法调用不频繁,这样做就可以。不过,如果调用非常频繁,可能还需要提高下性能,给 NSObject 加一个 category,里面实现这些 delegate 方法(有一点是它的方法都以 carousel: 开头,不容易跟本来已有的方法混淆,不然给 NSObject 加 category 可能会出现意料之外的结果),如果 delegate 没有实现对应的方法,自然就走到了 category 的方法中。
  • 就是在设置 delegate 的时候,把它对于每个 delegate 方法是否 respond 分别存一个标志位。最低限度,用一个 struct 来存,每个方法一个 bit 就可以,这样的好处是不用每次再询问是否 respond。有一个问题是,有可能在 delegate 设置之后,又通过 runtime 等方法增加了新的方法,从而改变了是否 respond delegate 方法的情况……如果是自己的工程里,知道自己一般不会这么作(一声),这样做就没问题;如果是封装控件给别人用,就可能要考虑这种情况 ><
9.@interface someClass(private) 这个用法,大家见过吗?~ 这是什么意思呢?~ 跟更为常见的 @interface someClass() 有什么区别呢?~
  • http://stackoverflow.com/questions/1052233/iphone-obj-c-anonymous-category-or-private-category
  • 加不加这个 (private) 是不是没区别。还是有一点区别的,加了之后会在 backtrace 显示 -[SomeClass(Private) someMethod]
  • @interface MyClass() 是编译时特性
  • @interface MyClass(private) 是运行时特性 #####10.

  • 答案是 第一个是 NSArray、第二个是 NSMutableArray 原因是 NSMutableDictionary 在 set key-value 的时候会把 key copy 一下。这就带来一个问题,不支持 NSCopying 协议的类当不了 key(而 NSCache 就没这个问题)
  • 在以上例子里,虽然 NSMutableArray 被 copy 成 NSArray 了,但是如果你传进去一个相同内容 NSMutableArray 的话还是能取出对应的值,这是因为 NSMutableArray 的 isEqual 实现没 bug
  • 而有些别的类就不一定如此了,一个例子就是 NSIndexPath,有时候我们想用 UITableView 的 indexpath 缓存一些东西,比如行高啊什么的。如果直接在 cellForRow 里面把它传过来的 indexPath 放到 NSDictionary 里面,下次取的时候可能就取不出来。因为它传过来的实际可能是个 NSMutableIndexPath,比较好的做法是 手动再生成一个 [NSIndexPath indexPathForRow…] 然后直接把这个不可变的扔进字典,下次以同样的方式生成 key 就肯定能取出来了。
11.用 NSCache 做缓存比跟 NSDictionary 有什么区别吧~~
  • NSCache 不会 copy key,而 NSDictionary 会之外,还有几条
  • NSCache 是线程安全的,NSDictionary 不是
  • NSCache 有过期策略,具体策略无人知晓,可以认为跟 cost、访问频率都有关。既可以限制最大缓存数,又可以限制最大内存。从这点来看是远比 NSDictionary 适合做缓存的,NSMutableArray 如果不加锁,几个线程一直往里写,中间可以出现一个 nil。NSMutableDictionary 我没试过,但肯定会出问题,而 get、set 方法加锁或者放到串行队列里就不会有线程安全问题了。猜想 NSCache 就是这么实现的 #####12.正好进度快到多线程了,用 NSOperationQueue 和gcd做有什么区别?- NsOperation好处大概就是这样
  • NSOperationQueue可以取消,gcd一般发出去就发出去了,也可以取消
  • NSOperationQueue可以指定依赖关系
  • NSOperationQueue可以限制最大并发数
  • Operation对象可以重用,在有些业务情境下
  • 可以有任务的优先级,gcd是队伍的优先级,NSOperation粒度细一些

Gcd的好处:

  • 写起来代码短,因为没那层包装开销小一些,
  • 可以取消, dispatch block cancel
13.dispatch_sync 如何造成死锁?能写出一个例子嘛?~ 为何会死锁?平常工程开发中,如何安全避免这种死锁?
  • SDWebImage的源码
    就是先判一下是否在当前线程,如果在的话就直接执行,否则再 dispatch。如果自己用得多,也可以加这个宏~ <只有主队列和自己写的串行队列会有这问题>
14.今天总结一下 dealloc 里该写什么和不该写什么,仓鼠写几条常见的,剩下的大家可以自己补充~

该写什么:

  • 从 NotificationCenter remove observer,remove KVO,把 timer invalidate,有些非 ARC 管理的比如 CFRelease,有些流要关闭,用了 RAC 持有 disposable 对象的可以 dispose,这些都是比较常见的
  • 还有一些会 removeGestureRecognizer,addToRunLoop 的 removeFromRunLoop,dispatch_release 掉自己持有的 queue,不过这些可能不是必要的
  • 有些人会取消掉这个对象应该接受的网络请求,当然如果没停掉的话,返回来应该也没什么关系
  • 另外一点是如果有 delegate 可以这一步设成 nil,比如自己写的 tableViewController 最好在 dealloc 里把 _tableView.delegate = nil、dataSource = nil。虽然现在 tableView 的 dataSource 和 delegate 是 weak 的,但这样似乎可以减少一些非常偶发的 bug……

不该做什么:

  • 看一些老代码的时候,里面会一口气把自己的所有 property 都设成 nil,看下来一大串 _xxx = nil; _xxx = nil; 这个在现在的 ARC 环境是已经没必要的了,不用浪费时间和篇幅来写这个。
  • 最好不要调用异步带回调的方法,因为等方法回来的时候这个对象肯定已经没了。
  • 不要调 super dealloc,实际上想调也调不了…… ARC 下编译器会自动调的
  • 在 init 方法和 dealloc 方法里都不要用点语法,而是直接用下划线 _property 进行存取。因为点语法会触发 getter、setter 方法,不知道这些方法会被重写成什么,可能会涉及或者触发一些不该在 dealloc 阶段做的事比如 KVO 之类
15.在 iOS 里如何实现同步机制吧~ 比如昨天偶然看到的一道面试题:如果让你实现 atomic 属性,你会怎么实现?
  • 首先最常使用的是 @synchronized 块,这是同步机制中最慢的一个,也是语法上最方便的一个。所以不太频繁的话用这个即可~
  • 然后一种思路是加锁。最简单的有 NSLock,要注意可能的死锁,为了解决可以考虑 NSRecursiveLock。这里有一篇文章介绍了几大常用的锁:http://www.jianshu.com/p/6c8bf19eb10d
  • 另一种思路是用事件队列。比如 gcd 可以创建 serial queue,扔进去的操作就会按顺序依次执行了。一个更高效的方法是用 barrier block,可以把 setter 方法设为 barrier,getter 方法还是普通的。这样 getter 方法可以并行,仍能取到安全数据,不会取到比如 set 到一半的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值