續(xù)寫iOS 面試題及答案20道21~40(二)
41.談?wù)剬C和Swift動態(tài)特性的理解
runtime其實就是OC的動態(tài)機制藤肢。runtime執(zhí)行的是編譯后的代碼,這時它可以動態(tài)加載對象瑟匆、添加方法憔杨、修改屬性、傳遞信息等。具體過程是督惰,在OC中,對像調(diào)用方法時旅掂,如[self.tableview reload],經(jīng)歷了兩個過程赏胚。
- 編譯階段: 編譯器會將OC代碼翻譯成objc_msgSend(self.tableview,@slector(reload)),把消息發(fā)送給self.tableview。
- 運行階段: 接收者(self.tableview)會響應(yīng)這個消息商虐,其間可能會直接執(zhí)行觉阅,轉(zhuǎn)發(fā)消息,也可能會找不到方法導(dǎo)致程序崩潰秘车。
所以典勇,整個流程是:編譯器翻譯->給接收者發(fā)送消息->接收者響應(yīng)消息。
接收者如何去響應(yīng)消息叮趴,就發(fā)生在運行時割笙。runtime執(zhí)行的是編譯后的代碼,而代碼的具體實現(xiàn)是運行時才能夠確定眯亦,這時它就可以動態(tài)加載對象伤溉、添加方法、修改屬性妻率、傳遞消息等乱顾。runtime的運行機制就是OC的動態(tài)特性。
Swift公認的是一個靜態(tài)語言宫静,它的動態(tài)特性都是通過橋接OC來實現(xiàn)的走净,像一些動態(tài)的特性其實是可以用Swift的面向協(xié)議的編程那樣使用的。
例子:
在OC中我們?nèi)討B(tài)的調(diào)用一個方法孤里,首先需要判斷這個方法是否存在然后再去調(diào)用它伏伯,這就是純正的動態(tài)特性:代碼
OC的動態(tài)特性
UserModel類的實現(xiàn)
@implementation UserModel
- (void)todoSomething{
NSLog(@"正在做什么事情。");
}
@end
動態(tài)調(diào)用UserModel的方法扭粱。
UserModel *user = [UserModel new];
if([user respondsToSelector:NSSelectorFromString(@"todoSomething")]){
[user performSelector:NSSelectorFromString(@"todoSomething")];
}
Swift的面向協(xié)議編程
面向協(xié)議的實現(xiàn)代碼
protocol TodoProtocol {
func todosomething()
}
class UserModel: TodoProtocol {
func todosomething() {
print("要做的事情")
}
}
調(diào)用代碼
let user:UserModel = UserModel()
if let user1 = user as? TodoProtocol {
user1.todosomething()
print("實現(xiàn)了這個協(xié)議")
}else{
print("沒有實現(xiàn)這個協(xié)議")
}
這樣來使用舵鳞,這個協(xié)議的方法,其實這步驟判斷不需要因為swift中的協(xié)議的方法都是required的沒有optional琢蛤。
42.談?wù)刟s蜓堕、as!抛虏、as? 含義是什么?
1)as的三種使用方式
- as第一種用法是將派生類轉(zhuǎn)換成基類套才,也就是說子類直接轉(zhuǎn)換成父類迂猴。 向上轉(zhuǎn)型。
代碼:
class Animal{
var name:String
init(_ name:String){
self.name = name
}
}
class catanimal : Animal {
}
class doganimal: Animal {
}
//as 向上強轉(zhuǎn) 子轉(zhuǎn)父
let cat = catanimal("湯姆") as Animal
let dog = doganimal("泰克") as Animal
showAnimalName(cat)
showAnimalName(dog)
如果是父類轉(zhuǎn)換成子類背伴,那么這種形式在Swift是會報錯的沸毁,就算是加上!強轉(zhuǎn)a!運行的時候也會崩潰。
- as第二種用法消除多義性傻寂,數(shù)據(jù)類型轉(zhuǎn)換
比如說
let AnimalAge = 3 as Int //3 有可能是Int 也有可能是CGFloat 也可能是long Int
let AnimalWeight = 20 as CGFloat
let AnimalHeight = (50 / 2) as Double
- as的第三種用法switch語句中進行模式匹配息尺,通過switch愈發(fā)檢測對象的類型,根據(jù)對象類型進行處理疾掰。
switch animalType{
case let animal as catanimal:
print("是catanimal類型")
case let animal as doganimal:
print("是doganimal類型")
default: break
}
2)as!的用法
向下轉(zhuǎn)型搂誉,強制轉(zhuǎn)換類型,有個缺點就是如果轉(zhuǎn)化失敗就會報錯誤静檬。
一般形式下可以這么使用炭懊,
catanimal是Animal的子類。
let animal:Animal = catanimal("湯姆")
let cat = animal as!catanimal
上述代碼就是將類型強制轉(zhuǎn)化拂檩,首先要注意一點侮腹,在強轉(zhuǎn)之前一定要判斷animal這個類是用catanimal這個類初始化的, 如果不這樣做的話稻励,是用Animal初始化的父阻,那么在第二行強制轉(zhuǎn)化的話就會報錯。
3)as?的用法
as?跟as!是一樣的用法钉迷,只不過as?如果轉(zhuǎn)化不成功的話會直接返回一個nil對象至非,成功的話返回可選類型。如果說能%100的會成功轉(zhuǎn)化那么請用as!如果說不能的話請用as?
使用例子:
let animal:Animal = catanimal("湯姆")
if let cat = animal as? catanimal {
print("是catanimal類型")
}else{
print("nil")
}
43.查看下述代碼輸出是什么糠聪?為什么荒椭?
protocol Police{
func HandlingCases()
}
extension Police{
func HandlingCases(){
print("Police doing Handling cases!" );
}
}
struct Judge:Police{
func HandlingCases(){
print("Judge doing Handling cases!" );
}
}
調(diào)用代碼
let police1:Police = Judge()
let police2:Judge = Judge()
police1.HandlingCases()
police2.HandlingCases()
兩個輸出的內(nèi)容都是Judge doing Handling cases!
,
因為在Swift中protocol聲明了某個方法,在沒有extension擴展協(xié)議的情況下必須在實現(xiàn)類中實現(xiàn)該方法舰蟆,swift中的協(xié)議的方法都是required的趣惠,如果extension中實現(xiàn)了該方法,則在實現(xiàn)類Judge
中可以不用去實現(xiàn)身害,一旦實現(xiàn)味悄,那么還是以實現(xiàn)類Judge
的實現(xiàn)方法為準(zhǔn)。
HandlingCases
方法在Police
協(xié)議中聲明了塌鸯,police1
雖然聲明的是Police
但是實際實現(xiàn)還是Judge
所以根據(jù)實際情況是調(diào)用了Judge
的HandlingCases
實現(xiàn)方法侍瑟。同樣道理police2
也是如此。
但是如果說在Police
中沒有生命方法HandlingCases
,其他不變的情況下涨颜,那么police1
费韭、police2
的輸出就會不一樣了。因為police1
的實際類型是Police
庭瑰,Police
中并沒有聲明HandlingCases
,但是在類擴展中有實現(xiàn)該方法星持,實際類調(diào)用實際的方法,那么就會調(diào)用擴展類中實現(xiàn)HandlingCases
弹灭,而police2
的實際類型是Judge
那肯定就會調(diào)用Judge
中的實現(xiàn)方法督暂。
所以他們的輸出是:
police1
輸出 Police doing Handling cases!
police2
輸出 Judge doing Handling cases!
44.message send如果找不到對象,則會如何進行后續(xù)處理
這種形式一般會有兩種情況:1)對象是nil穷吮; 2)對象不為nil但是就是找不到對應(yīng)的方法逻翁;
1)對象為空的時候,在OC中向一個nil的對象發(fā)送消息是可以的酒来,如果這個方法的返回值是對象那么返回的是nil卢未,如果返回值是結(jié)構(gòu)體肪凛,那么就是0.
2)對象不為空堰汉,找不到方法,就會崩潰伟墙,報錯翘鸭。
45.method swizzling 是什么?
每一個類都會維護一個方法列表戳葵,并且方法名跟方法實現(xiàn)是一一對應(yīng)的就乓,也就是SEL(方法名)和IMP(方法實現(xiàn)的指針)的對應(yīng)關(guān)系,
method swizzling的意義就是運用runtime的特性跟方法來進行SEL和IMP這一對的IMP進行更換操作拱烁。如果SELa對應(yīng)IMPa SELb對應(yīng)IMPb 使用method swizzling后可以成為SELa對應(yīng)IMPb SELb對應(yīng)IMPa生蚁。
代碼實現(xiàn)
在本類實現(xiàn)2個方法:
- (void)onefunc{
NSLog(@"one");
}
- (void)twofunc{
NSLog(@"two");
}
在本類中調(diào)用代碼
SEL one = @selector(onefunc);
Method OneMethod = class_getInstanceMethod([self class], one);
SEL two = @selector(twofunc);
Method TwoMethod = class_getInstanceMethod([self class], two);
method_exchangeImplementations(OneMethod, TwoMethod);
這樣就可以看到調(diào)用onefunc會打印two, 調(diào)用twofunc會打印one
46.Swift和OCjective-C的自省(Introspection)有什么不同
自省在Objective-C中就是:判斷一個對象是否屬于某個類的操作戏自。它有兩種形式邦投。
[objc isKindOfClass:[SomeClass class]];
[objc isMemberOfClass:[SomeClass class]];
第一個判斷isKindOfClass
是判斷objc是否為SomeClass或者其子類的實例對象。
第二個判斷isMemberOfClass
是判斷objc僅僅是SomeClass這個類(非子類擅笔,當(dāng)前類)的實例對象志衣,并且不能檢測任何類都是基于NSObject類。
這兩種判斷的前提是objc必須是NSObject的子類猛们。
Swift中只有isKindOfClass
類似的方法is
念脯,很多的類并不是繼承自NSObject,不過比OC的功能更加強大弯淘,is
可以判斷enum绿店、struct類型
自省操作一般和動態(tài)類型一起出現(xiàn),比如說OC中的id類型庐橙,以及Swift中的可選類型假勿、anyobject惕它。
cat是animal的子類
id animal = catInstance;
if([animal isKindOfClass:[animal class]]){
NSLog(@"是 animal class");
if(animal isMemberOfClass:[cat class]){
NSLog(@"是 cat class");
}
}else if([animal isKindOfClass:[any(其他類) class]]){
NSLog(@"是 其他 class");
}
47.能否通過Category給已有的類添加屬性(property)
無論是Swift還是OC都可以用Category來添加屬性,只不過添加的方式不一樣废登。
Objective-C:
OC中通過Category中直接添加屬性(property)會報錯淹魄,提示找不到getter和setter方法,那是因為在Category中不會自動生成這兩個方法堡距,解決的方法就是運用runtime甲锡,關(guān)聯(lián)對象的形式來添加屬性,主要涉及到的兩個函數(shù)是羽戒,objc_getAssocicatedObject和objc_setAssociatedObject.
objc_getAssocicatedObject兩個參數(shù): 本類實例對象缤沦、 關(guān)聯(lián)屬性的key
objc_setAssociatedObject中的方法有4個參數(shù),分別是 本類實例對象易稠、關(guān)聯(lián)屬性的key缸废、新值、關(guān)聯(lián)策略驶社。
@interface UserModel : NSObject
@end
@implementation UserModel
@end
@interface UserModel(en)
@end
#import "UserModel+En.h"
#import <objc/runtime.h>
static void *EnNameKey = &EnNameKey;
@implementation UserModel(en)
-(void)setEnName:(NSString *)EnName{
objc_setAssociatedObject(self, &EnNameKey, EnName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (NSString *)EnName{
return objc_getAssociatedObject(self, &EnNameKey);
}
@end
Swift跟Objective-C的使用方式差不多企量,如下代碼
class UserModel {
var Name:String = "小明"
}
var EnNameKey:Void?
extension UserModel{
var EnName:String? {
get{
return objc_getAssociatedObject(self, &EnNameKey) as? String
}
set{
objc_setAssociatedObject(self, &EnNameKey, "你的英文名字", objc_AssociationPolicy.OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
}
48.談?wù)効磳code你有多少理解。
iOS開的IDE是Xcode亡电,它是apple開發(fā)的主流工具届巩,目前Xcode已經(jīng)更新到了9個版本。功能蘊含了開發(fā)份乒、測試恕汇、性能分析、文檔查詢或辖、源代碼管理等多方面功能瘾英。
C、C++與Objective-C密不可分颂暇,自動化用Ruby缺谴,熟悉的工具:fastlane、cocoapods蟀架、Automation工具的腳本大多是用javaScript瓣赂,剛發(fā)布的coreML采用的模型工具則是用Python開發(fā)的。最新的Xcode采用完全由Swift重寫的Source Editor片拍,在代碼修改煌集、補全、模擬器運行方面有了很大的提升捌省,目前苫纤,Xcode最大的缺點是穩(wěn)定性還不夠。
Xcode工具想用的熟練,則必須從Intruments性能分析和LLDB調(diào)試卷拘,一步一步進行由淺入深喊废、Swift最新的Playground也是一個不錯的工具。
49.LLDB中的p和po的區(qū)別
- p是exor的縮寫栗弟。它的工作是把接受到的參數(shù)在當(dāng)前環(huán)境下運行編譯污筷,人后打印出對應(yīng)的值。
- Po即expr-o-乍赫。操作跟p相同瓣蛀,如果接收的是一個指針,那么它會調(diào)用對象的description方法并打永壮А惋增;如果接收到的參數(shù)是一個core foundation對象,那么它會調(diào)用CFShow方法并打印改鲫,如果兩個方法都調(diào)用失敗鸳吸,那么po打印出和p相同的內(nèi)容铭拧。
- Po相對于p來說可以多打印一些內(nèi)容判导,一般用p即可疙筹,畢竟打印的東西越少越快,效率越高讲弄。如果需要查看詳情就用Po措左。
50.description方法是干什么用的?
這個是iOS對象默認的一個方法避除,它的輸出格式一般為<類名: 對象的內(nèi)存地址>
也可以自定義這個輸出格式,NSObject類所包含胸嘁。
51.Xcode中的Buildtime issues和Runtime issues指的是什么瓶摆?
Buildtime issues一般分為三類:編譯器識別出的警告(Warning)、錯誤(Error)和靜態(tài)分析(Static Code Analysis)性宏。前兩者一般經(jīng)常會遇到群井,不用多說,靜態(tài)分析也可以分為三種:1)未初始化變量毫胜,未使用數(shù)據(jù)和API使用錯誤书斜。
Swift代碼:
class CusViewController: UIViewController {
override func viewDidLoad() {
let PeopleList:[UserModel]
let somePeopleList = PeopleList
let otherPeopleList:[UserModel]?
}
}
分析代碼:
PeopleList并沒有初始化就去賦值給somePeopleList所以是未初始化報錯。
otherPeopleList并沒有使用酵使,那么就會出現(xiàn)未使用的數(shù)據(jù)荐吉,在viewDidLoad中沒有使用super.viewDidLoad()那么就是Api調(diào)用錯誤。
Runtime issues也有三類錯誤:線程問題口渔、UI布局和渲染問題样屠、內(nèi)存問題。線程的相關(guān)問題最多,最常見的就是數(shù)據(jù)的競爭痪欲。
var num = 0
let addnum1 = 10,addnum2 = 100
DispatchQueue.global().async {
for _ in 0...10{
num += addnum1
}
}
for _ in 0...10 {
num += addnum2
}
兩個線程都對num進行寫操作悦穿,這樣的話,誰先操作业踢,那么值就會根據(jù)方法+幾 因為數(shù)據(jù)直接就存在了爭搶關(guān)系栗柒,當(dāng)然最終結(jié)果是一樣的,但是中間的先后順序就會打亂了知举。
UI布局和渲染上面的時候尺寸設(shè)定傍衡、布局沒有給全,渲染設(shè)定模糊负蠕,因而造成的autolayout無法渲染蛙埂。
內(nèi)存上的問題就是內(nèi)存泄漏,比如循環(huán)引用等遮糖。
52.App啟動時間過長绣的,該怎么去優(yōu)化?
導(dǎo)致App啟動時間過長的原因有多種欲账,從理論上面來講有兩種情況:1)main函數(shù)加在之前屡江;2)main還是加載之后。
main 函數(shù)加載之前赛不,如果想要分析這塊兒的代碼惩嘉,需要去Xcode中添加DYLD_PRINT_STATISTICS環(huán)境變量,并將其值設(shè)置為1踢故,這樣就可以得到如下的啟動日志:
還有很多的靜態(tài)變量文黎,如果想查看的話,在終端man dyld
會打印出你要的靜態(tài)變量的列表殿较,可以一個一個的去打印看看耸峭。
例子:
Total pre-main time: 339.26 milliseconds (100.0%) //總的時間 毫秒
dylib loading time: 154.24 milliseconds (45.4%) //庫加載
rebase/binding time: 78.42 milliseconds (23.1%) //重定向/綁定
ObjC setup time: 69.27 milliseconds (20.4%) //對象設(shè)置
initializer time: 37.18 milliseconds (10.9%) //初始化
slowest intializers :
libSystem.B.dylib : 8.49 milliseconds (2.5%) //系統(tǒng)庫
libMainThreadChecker.dylib : 17.09 milliseconds (5.0%) //系統(tǒng)庫
從上面打印的內(nèi)容來看,大致就是上面的四個方面: 動態(tài)庫加載淋纲、重定位綁定對象劳闹、設(shè)置對象、對象的初始化洽瞬。
通過上述打印我們可以通過以下方式來優(yōu)化App的啟動時間:
減少動態(tài)庫的數(shù)量本涕,動態(tài)庫加載時間會減少,apple推薦的動態(tài)庫數(shù)量不超過6個伙窃。
減少Objective-C的類數(shù)量菩颖,例如合并和刪除,這樣可以加快動態(tài)鏈接对供,重定位/綁定耗費的時間會相應(yīng)的減少位他。
使用initialize方法替代load方法氛濒,或盡量將load方法中的代碼延后調(diào)用。對象的初始化所耗費的時間會相應(yīng)減少鹅髓。
在main之后的app啟動時間主要是要優(yōu)化第一個界面的渲染速度舞竿,主要是看進入Viewdidload viewwillAppear這兩個方法是否有其他操作。
53.如何檢測代碼中的循環(huán)引用窿冯?
目前所了解的有兩種方式:
1) 使用Xcode中的Memory Debug Graph骗奖。在Xcode中運行代碼,在有可能循環(huán)引用的地方添加斷點醒串,然后點擊如圖所示的按鈕就能查看是否循環(huán)引用执桌。
上圖中的內(nèi)容,點擊了這個按鈕以后左邊是類芜赌,右邊是類圖仰挣,其中誰引用了誰,這里很清楚的可以看到引用示意圖缠沈。
2) 使用Instruments里面的leaks選項--這是一個專門檢測內(nèi)存泄漏的工具膘壶,在進入首頁以后,發(fā)現(xiàn)leak Checks中出現(xiàn)內(nèi)存泄漏洲愤,就是出現(xiàn)小紅點的時候颓芭,可以將導(dǎo)航欄中的選項切換到call tree模式下,如果調(diào)試自己寫的代碼的話柬赐,建議勾選Display Settings 中勾選 "Separate by Thread"和"Hide System Libraries"兩個選項亡问。這樣可以隱藏系統(tǒng)和應(yīng)用程序本身的調(diào)用路徑,從而更方便地找出retain cycle位置肛宋。
54.怎么解決EXC_BAD_ACCESS
產(chǎn)生EXC_BAD_ACCESS的主要原因是訪問了某些已經(jīng)被釋放掉的對象州藕,或者訪問了它們已經(jīng)釋放的成員變量或方法。解決方法主要有下面幾種:
- 設(shè)置全局斷點悼吱,快速定位缺陷所在:這種方法效果一般慎框。
- 重寫Object和respondsToSelector方法:這個方法效果一般,并且要在每個class上進行定點排查后添,所以不推薦使用該方法。
- 使用Zombie和Address Sanitizer:可以在絕大多數(shù)情況下定位問題代碼薪丁,
55.如何在Playground中執(zhí)行異步操作
閱讀下述代碼遇西,打印結(jié)果是什么?
import Foundation
let urlstr = URL.init(string: "https://api.apiopen.top/getSingleJoke")
let task = URLSession.shared.dataTask(with: urlstr!) { (data, response, error) in
do {
let dict:NSDictionary = try JSONSerialization.jsonObject(with: data!, options: []) as! NSDictionary
print(dict.object(forKey: "message") as! String)
print(dict)
} catch {
print(error)
}
} //這個是初始化任務(wù)严嗜,但是并沒有執(zhí)行粱檀,下面必須調(diào)用恢復(fù),證明這個任務(wù)已經(jīng)開始只不過暫停漫玄,字面意思哈茄蚯。
task.resume() //恢復(fù)任務(wù)
答案是:什么都不會打印出來压彭,原因是playground在執(zhí)行完所有的操作以后會自動退出,要讓playground打印出異步執(zhí)行的內(nèi)容渗常,需要具備延時運行的特征壮不,所以需要在Playground中添加下述代碼:
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
56.在Playground中實現(xiàn)一個10行的列表,每行顯示一個0~100的整數(shù)皱碘。
啥也不說了上代碼
import UIKit
import PlaygroundSupport
class ViewController :UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
}
}
extension ViewController {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.text = "\(Int(arc4random_uniform(100)))"
return cell
}
}
PlaygroundPage.current.liveView = ViewController()
57.runtime如何實現(xiàn)weak變量的自動置nil询一?
//問題延伸
Weak指針不會增加所引用對象的引用計數(shù),并且在引用對象被回收的時候自動置nil癌椿,通常解決循環(huán)引用的問題健蕊,自動置nil的原理是什么?
Runtime維護了一個Weak的表踢俄,用于存儲指向某個對象的所有Weak指針缩功。Weak表就是一個hash表(哈希表),Key是所指對象地址都办,Value是Weak指針的地址(這個地址的值是所指對象的地址)的數(shù)組嫡锌。
在對象被回收的時候,經(jīng)過層層調(diào)用脆丁,會最終將這個Weak表中所有的Weak指針全部置nil世舰。
源碼在Runtime的源碼中有個文件objc-weak.mm 其中就有這段代碼。
/**
* Called by dealloc; nils out all weak pointers that point to the
* provided object so that they can no longer be used.
*
* @param weak_table
* @param referent The object being deallocated.
*/
void
weak_clear_no_lock(weak_table_t *weak_table, id referent_id)
{
objc_object *referent = (objc_object *)referent_id;
weak_entry_t *entry = weak_entry_for_referent(weak_table, referent);
if (entry == nil) {
/// XXX shouldn't happen, but does with mismatched CF/objc
//printf("XXX no entry for clear deallocating %p\n", referent);
return;
}
// zero out references
weak_referrer_t *referrers;
size_t count;
if (entry->out_of_line()) {
referrers = entry->referrers;
count = TABLE_SIZE(entry);
}
else {
referrers = entry->inline_referrers;
count = WEAK_INLINE_COUNT;
}
for (size_t i = 0; i < count; ++i) {
objc_object **referrer = referrers[i];
if (referrer) {
if (*referrer == referent) {
*referrer = nil;
}
else if (*referrer) {
_objc_inform("__weak variable at %p holds %p instead of %p. "
"This is probably incorrect use of "
"objc_storeWeak() and objc_loadWeak(). "
"Break on objc_weak_error to debug.\n",
referrer, (void*)*referrer, (void*)referent);
objc_weak_error();
}
}
}
weak_entry_remove(weak_table, entry);
}
根據(jù)對象地址槽卫,找到weak指針的數(shù)組跟压,遍歷所有數(shù)組的weak指針置nil,最后將這個entry從weak表中刪除 weak_entry_remove(weak_table, entry);
58.介紹一下GCD歼培、NSOperation震蒋、NSTread分別是是什么?NSOperation和GCD比有哪些優(yōu)缺點躲庄?
GCD是一個基于C語言的多線程編程的解決方式查剖。NSOperation是更加注重面向?qū)ο蟮亩嗑€程編程的解決方式。NSTread是一個輕量級的多線程編程方式噪窘。
NSOperation更加抽象化笋庄,抽象化的方式可以使多線程管理并發(fā),順序倔监,依賴關(guān)系時更加靈活:
- 性能上:GCD接近底層直砂,基于C語言的代碼執(zhí)行更加高效,理論上速度比NSOperation快
- 異步隊列操作上浩习,管理順序静暂、依賴關(guān)系,這些面向?qū)ο蟮木幊虝臃奖闳崿F(xiàn)谱秽,而GCD在這種方式上面會更加麻煩洽蛀,代碼量會更多摹迷。
- 日常使用上,如果是簡單的異步操作回調(diào)的形式建議是GCD郊供,簡單方便峡碉,如果對一些異步操作管理過程中有更多的線程順序、依賴關(guān)系那么建議用NSOperation
最后扯個犢子颂碘, 這玩意就是看你用的哪個熟悉异赫。只要能夠快速準(zhǔn)確的實現(xiàn),用什么都是一樣的头岔。
59.怎么用Copy關(guān)鍵字
copy一般用在NSString塔拳、NSArray、NSDictionary等屬性字段的修飾符峡竣。
因為這些屬性都有與之對應(yīng)可變的子類靠抑。用copy修飾的上述幾個類型,在賦值的時候有坑會賦到可變的類型的指針适掰,如果這個可變子類用strong修飾颂碧,那么一旦這個可變對象的值被修改了,那么這個對象也就被修改了类浪,所以copy就是為了復(fù)制一份不可變的對象付給copy修飾的對象载城。
//mutableString這個參數(shù)是類A的一個變量類型是NSMutableString
NSMutableString* mutableString = [[NSMutableString alloc]initWithString:@"mutablestring"];
UserModel *user = [[UserModel alloc]init];
//首先將字符串賦值
user.title = @"title";
user.title = mutableString; //這時候title==mutableString==@"mutablestring"
//修改mutableString的值
[mutableString appendString:@"111"];
//得到的結(jié)果是user.title == mutableString == @"mutablestring111"
NSLog(@"title=%@--%p mutableString=%@--%p",user.title,user.title,mutableString,mutableString);
最終 title在不知不覺中就被修改了。所以用copy费就,修飾出來的屬性都是不可變的诉瓦。
在setter方法中:
- (void)setStr:(NSString*)str
if(_Str != str){
[_Str release];
_Str = [str copy];
}
60.什么是MVC、MVP力细、MVVM睬澡?MVVM的每一層關(guān)系是什么?
MVC 是Model眠蚂、View煞聪、Controller。
View與Controller通信逝慧、Model與Controller通信 View與Model嚴格意義上是完全解耦合的昔脯。
-
View與Controller:
- action:點擊、滑動笛臣、跳轉(zhuǎn)栅干、刷新UI、網(wǎng)絡(luò)請求等捐祠,用戶與View交互觸發(fā)、控制器方法
- 委托delegate:View向Controller詢問一些自己無法做到的事情桑李,讓Controller去解決踱蛀,比如說獲取數(shù)據(jù)
- 數(shù)據(jù)Model:View跟Controller要需要顯示的數(shù)據(jù)窿给,Controller需要訪問Model,從Model中獲取數(shù)據(jù)告訴View讓其顯示
-
Model與Controller
- 廣播(Notification)率拒,Controller注冊監(jiān)聽Model的數(shù)據(jù)變化的通知崩泡,當(dāng)Model變化的時候告訴ControllerModel數(shù)據(jù)更新了
- KVO(KEY-Value-Observing):Model作為Controller的屬性,Controller監(jiān)聽這個屬性變化猬膨,當(dāng)Model屬性被修改時角撞,這個Controller會接收到通知
分離了視圖層和業(yè)務(wù)層
耦合性低
開發(fā)速度快
可維護性高
MVP 是Model-View(Controller)-Presenter
MVP模式也是一種經(jīng)典的界面模式。MVP中的M代表Model, V是View, P是Presenter勃痴。在MVP里,Presenter完全把Model和View進行了分離沛申,主要的程序邏輯在Presenter里實現(xiàn)。而且尖淘,Presenter與具體的View是沒有直接關(guān)聯(lián)的,而是通過定義好的接口(協(xié)議)進行交互著觉,從而使得在變更View時候可以保持Presenter的不變,重用
優(yōu)點
低耦合
可重用性
獨立開發(fā)
可測試
缺點
如果view跟Presenter的交互太過于頻繁饼丘,那就會跟特定的界面過于緊密,如果視圖變更葬毫,那么presenter也要跟著變更了镇辉。
MVVM 是 Model View ViewModel
Model:業(yè)務(wù)邏輯處理贴捡、數(shù)據(jù)控制(本地緩存、網(wǎng)絡(luò)加載)
View(Controller):顯示用戶可見烂斋、用戶交互
ViewModel:組織View和Model的邏輯層
View綁定到ViewModel汛骂,然后執(zhí)行一些命令在向它請求一個動作。
ViewModel跟Model通訊帘瞭,反過來ViewModel在告訴View更新UI
優(yōu)點
低耦合
可重用性
獨立開發(fā)
可測試
缺點
因為ViewModel跟Presenter不一樣的是所有的數(shù)據(jù)都是協(xié)議蝶念、接口回調(diào),而ViewModel則是數(shù)據(jù)綁定到View上面担敌,如果是數(shù)據(jù)bug問題全封,很難定位到數(shù)據(jù)哪一步出錯马昙。
在一個模塊中 viewModel中如果Model的數(shù)據(jù)很大刹悴,長期不釋放颂跨,這是一個不必要的花銷。
數(shù)據(jù)雙向綁定不利于代碼的重用恒削,開發(fā)中常見的重用都是view钓丰,一個view中綁定一個model,不同的模塊中的model不同琢歇,到時候如果只簡單的重用view梦鉴,Model不做任何變化的話不太現(xiàn)實肥橙。