- 設(shè)計模式是什么蟋恬? 你知道哪些設(shè)計模式,并簡要敘述榕暇?
設(shè)計模式是一種編碼經(jīng)驗见剩,就是用比較成熟的邏輯去處理某一種類型的事情杀糯。
1). MVC模式:Model View Control扫俺,把模型 視圖 控制器 層進行解耦合編寫苍苞。
2). MVVM模式:Model View ViewModel 把模型 視圖 業(yè)務(wù)邏輯 層進行解耦和編寫。
3). 單例模式:通過static關(guān)鍵詞狼纬,聲明全局變量羹呵。在整個進程運行期間只會被賦值一次。
4). 觀察者模式:KVO是典型的通知模式疗琉,觀察某個屬性的狀態(tài)冈欢,狀態(tài)發(fā)生變化時通知觀察者。
5). 委托模式:代理+協(xié)議的組合盈简。實現(xiàn)1對1的反向傳值操作凑耻。
6). 工廠模式:通過一個類方法,批量的根據(jù)已有模板生產(chǎn)對象柠贤。
-
MVC 和 MVVM 的區(qū)別
MVC是一種架構(gòu)模式香浩,M表示Model,V表示視圖View臼勉,C表示控制器Controller:
- Model負責(zé)存儲邻吭、定義、操作數(shù)據(jù)宴霸;
- View用來展示給用戶囱晴,并且和用戶進行交互膏蚓;
- Controller是Model和View的協(xié)調(diào)者,Controller把Model中的數(shù)據(jù)拿過來給View使用畸写。Controller可以直接與Model和View進行通信驮瞧,而View不能與Controller直接通信。艺糜,當有數(shù)據(jù)更新時剧董,Model也要與Controller進行通信,這個時候就要用Notification和KVO破停,這個方式就像發(fā)廣播一樣翅楼,Model發(fā)信號,Controller設(shè)置接收監(jiān)聽信號真慢,當有數(shù)據(jù)更新是就發(fā)信號給Controller毅臊,Model和View不能直接通信,這樣違背MVC設(shè)計原則黑界。View與Controller通信需要利用代理協(xié)議的方式管嬉,Controller可以直接根據(jù)Model決定View的展示。View如果接受響應(yīng)事件則通過delegate朗鸠,target-action蚯撩,block等方式告訴Controller的狀態(tài)變化。Controller進行業(yè)務(wù)的處理烛占,然后再控制View的展示胎挎。
那這樣Model和View就是相互獨立的。View只負責(zé)頁面的展示忆家,Model只是數(shù)據(jù)的存儲犹菇,那么也就達到了解耦和重用的目的。
實例:假設(shè)蘋果根據(jù)買iPhone的人給予不同的優(yōu)惠芽卿,學(xué)生優(yōu)惠20%揭芍,it民工優(yōu)惠50%,其他不優(yōu)惠卸例。- MVVM設(shè)計模式
MVVM就是幫忙分擔一下controller里面的部分業(yè)務(wù)邏輯称杨。
[圖片上傳失敗...(image-3de556-1536114902062)]這個時候,controller將不再直接和真實的model進行綁定了筷转,而通過ViewModel,viewModel進而持有真實的Model姑原。
實例:看到修改完的代碼,你會發(fā)現(xiàn)VC里面已經(jīng)省去了不少的代碼旦装。一切都和viewModel進行交流页衙。這里我只是展示一個最簡單的數(shù)據(jù)展示,如果有其他響應(yīng)事件,是需要viewModel開放方法來進行處理的店乐,并要通知VC處理結(jié)果的艰躺。
關(guān)于MVVM的優(yōu)點:
- 方便測試
在MVC下,Controller基本是無法測試的眨八,里面混雜了個各種邏輯腺兴,而且分散在不同的地方。有了MVVM我們就可以測試里面的viewModel廉侧,來驗證我們的處理結(jié)果對不對(Xcode7的測試已經(jīng)越來越完善了)页响。
- 便于代碼的移植
比如iOS里面有iPhone版本和iPad版本,除了交互展示不一樣外段誊,業(yè)務(wù)邏輯的model是一致的闰蚕。這樣,我們就可以以很小的代價去開發(fā)另一個app连舍。
- 兼容MVC
MVVM是MVC的一個升級版没陡,目前的MVC也可以很快的轉(zhuǎn)換到MVVM這個模式。VC可以省去一大部分展示邏輯索赏。
缺點:
- 類會增多
每個VC都附帶一個viewModel盼玄,類的數(shù)量*2
- viewModel會越來越龐大
我們把邏輯給了viewModel,那勢必Model也會變得很復(fù)雜潜腻,里面的屬性和方法越來越多埃儿。可能重寫的方法比較多融涣,因為涉及到一些數(shù)據(jù)的轉(zhuǎn)換以及和controller之間的通信童番。
- 調(diào)用復(fù)雜度增加
由于數(shù)據(jù)都是從viewModel來,想想突然來了一個新人暴心,一看代碼妓盲,不知道真實的模型是誰杂拨。比如常用tableview的數(shù)據(jù)源专普,一般都是一個數(shù)組,如果不斷的通過viewModel去取弹沽,溝通上沒有那么直接檀夹。況且每封一層,意味著要寫很多代碼去融合他們的轉(zhuǎn)換策橘。
-
#import跟 #include 有什么區(qū)別炸渡,@class呢,#import<> 跟 #import””有什么區(qū)別丽已?
(1)#import指令是Object-C針對@include的改進版本蚌堵,能確保引用的文件只會被引用一次,不會陷入遞歸包含的問題中;
(2)@import與@class的區(qū)別:
#import會鏈入該頭文件的全部信息吼畏,包括實體變量和方法等督赤;二@class只是告訴編譯器,其后面聲明的名稱是類的名稱泻蚊,至于這些類如何定義的躲舌,暫時不用考慮。在頭文件中性雄,一般只需要知道被引用的類的名稱就可以了没卸,不需要知道其內(nèi)部的實體變量和方法,所以在頭文件中一般使用@class來聲明這個名稱是類的名稱秒旋;而在實現(xiàn)類里面约计,因為會用到這個引用類的內(nèi)部的實體變量和方法,所以需要使用#import類包含這個被引用類的頭文件迁筛。
@class還可以解決循環(huán)包含的問題
(3)#import<>跟#import""的區(qū)別:
import<>用來包含系統(tǒng)自帶的文件病蛉,#import""用來包含自定義的文件
(4)屬性readwrite,readonly瑰煎,assign铺然,retain,copy酒甸,nonatomic 各是什么作用魄健,在那種情況下用?
? readwrite:是可讀可寫特性插勤,同時生成get方法和set方法的聲明和實現(xiàn)(補充:默認屬性沽瘦,將生成不帶額外參數(shù)的getter和setter方法(setterff只有一個參數(shù)))
? readonly:只讀特性,只會生成get方法的聲明和實現(xiàn)农尖;不希望屬性在類外改變
? assign:是賦值特性析恋,set方法的實現(xiàn)是直接賦值,用于基本數(shù)據(jù)類型盛卡;僅設(shè)置變量時
? retain:表示持有特性助隧,set方法將傳入?yún)?shù)先保留,再賦值滑沧,傳入?yún)?shù)的retaincount會+1并村;
? copy:表示拷貝特性,set方法的實現(xiàn)是release舊值滓技,copy新值哩牍,用于NSString、block等類型(set方法將傳入的對象復(fù)制一份令漂;需要完全一份新的變量時使用)膝昆;
? nonatomic:非原子操作丸边,決定編譯器生成的setter getter是否是原子操作,atomic表示多線程安全荚孵,一般使用nonatomic
frame 和 bounds 有什么不同原环?
frame指的是:該view在父view坐標系統(tǒng)中的位置和大小。(參照點是父view的坐標系統(tǒng))
bounds指的是:該view在本身坐標系統(tǒng)中的位置和大小处窥。(參照點是本身坐標系統(tǒng))
- Objective-C的類可以多重繼承么嘱吗?可以實現(xiàn)多個接口么?Category是什么滔驾?重寫一個類的方式用繼承好還是分類好谒麦?為什么?
答:Objective-C的類不可以多重繼承哆致;可以實現(xiàn)多個接口(協(xié)議)绕德;Category是類別;一般情況用分類好摊阀,用Category去重寫類的方法耻蛇,僅對本Category有效,不會影響到其他類與原有類的關(guān)系胞此。
-
@property 的本質(zhì)是什么臣咖?ivar、getter漱牵、setter 是如何生成并添加到這個類中的
@property 的本質(zhì)是什么夺蛇?
@property = ivar + getter + setter;
“屬性” (property)有兩大概念:ivar(實例變量)、getter+setter(存取方法)“屬性” (property)作為 Objective-C 的一項特性酣胀,主要的作用就在于封裝對象中的數(shù)據(jù)刁赦。 Objective-C 對象通常會把其所需要的數(shù)據(jù)保存為各種實例變量。實例變量一般通過“存取方法”(access method)來訪問闻镶。其中甚脉,“獲取方法” (getter)用于讀取變量值,而“設(shè)置方法” (setter)用于寫入變量值铆农。
@property中有哪些屬性關(guān)鍵字牺氨?/ @property 后面可以有哪些修飾符?
屬性可以擁有的特質(zhì)分為四類:
1.原子性--- nonatomic 特質(zhì)
2.讀/寫權(quán)限---readwrite(讀寫)顿涣、readonly (只讀)
3.內(nèi)存管理語義---assign波闹、strong酝豪、 weak涛碑、unsafe_unretained、copy
4.方法名---getter=<name> 孵淘、setter=<name>
5.不常用的:nonnull,null_resettable,nullable屬性關(guān)鍵字 readwrite蒲障,readonly,assign,retain揉阎,copy庄撮,nonatomic 各是什么作用,在那種情況下用毙籽?
答:
1). readwrite 是可讀可寫特性洞斯。需要生成getter方法和setter方法。
2). readonly 是只讀特性坑赡。只會生成getter方法,不會生成setter方法,不希望屬性在類外改變赘那。
3). assign 是賦值特性评雌。setter方法將傳入?yún)?shù)賦值給實例變量;僅設(shè)置變量時,assign用于基本數(shù)據(jù)類型。
4). retain(MRC)/strong(ARC) 表示持有特性螟加。setter方法將傳入?yún)?shù)先保留徘溢,再賦值,傳入?yún)?shù)的retaincount會+1捆探。
5). copy 表示拷貝特性然爆。setter方法將傳入對象復(fù)制一份,需要完全一份新的變量時黍图。
6). nonatomic 非原子操作施蜜。決定編譯器生成的setter和getter方法是否是原子操作,atomic表示多線程安全雌隅,一般使用nonatomic翻默,效率高。
- 什么情況使用 weak 關(guān)鍵字恰起,相比 assign 有什么不同修械?
1.在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性。
2.自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak检盼;當然肯污,也可以使用strong。
IBOutlet連出來的視圖屬性為什么可以被設(shè)置成weak?
因為父控件的subViews數(shù)組已經(jīng)對它有一個強引用吨枉。
不同點:
assign 可以用非 OC 對象蹦渣,而 weak 必須用于 OC 對象。
weak 表明該屬性定義了一種“非擁有關(guān)系”貌亭。在屬性所指的對象銷毀時柬唯,屬性值會自動清空(nil)。
- 怎么用 copy 關(guān)鍵字圃庭?
用途:
1. NSString锄奢、NSArray失晴、NSDictionary 等等經(jīng)常使用copy關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString拘央、NSMutableArray涂屁、NSMutableDictionary;
2. block 也經(jīng)常使用 copy 關(guān)鍵字灰伟。
說明:
block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的拆又,但寫上 copy 也無傷大雅,還能時刻提醒我們:編譯器自動對 block 進行了 copy 操作栏账。如果不寫 copy 遏乔,該類的調(diào)用者有可能會忘記或者根本不知道“編譯器會自動對 block 進行了 copy 操作”,他們有可能會在調(diào)用之前自行拷貝屬性值发笔。這種操作多余而低效盟萨。
- 用@property聲明的 NSString / NSArray / NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,為什么了讨?如果改用strong關(guān)鍵字捻激,可能造成什么問題?
答:用 @property 聲明 NSString前计、NSArray胞谭、NSDictionary 經(jīng)常使用 copy 關(guān)鍵字,是因為他們有對應(yīng)的可變類型:NSMutableString男杈、NSMutableArray丈屹、NSMutableDictionary,他們之間可能進行賦值操作(就是把可變的賦值給不可變的)伶棒,為確保對象中的字符串值不會無意間變動旺垒,應(yīng)該在設(shè)置新屬性值時拷貝一份。
1\. 因為父類指針可以指向子類對象,使用 copy 的目的是為了讓本對象的屬性不受外界影響,使用 copy 無論給我傳入是一個可變對象還是不可對象,我本身持有的就是一個不可變的副本肤无。
2\. 如果我們使用是 strong ,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性先蒋。
//總結(jié):使用copy的目的是,防止把可變類型的對象賦值給不可變類型的對象時宛渐,可變類型對象的值發(fā)送變化會無意間篡改不可變類型對象原來的值竞漾。
- 淺拷貝和深拷貝的區(qū)別?
答:
淺拷貝:只復(fù)制指向?qū)ο蟮闹羔樋妫粡?fù)制引用對象本身业岁。
深拷貝:復(fù)制引用對象本身。內(nèi)存中存在了兩份獨立對象本身寇蚊,當修改A時笔时,A_copy不變。
- 系統(tǒng)對象的 copy 與 mutableCopy 方法
不管是集合類對象(NSArray幔荒、NSDictionary糊闽、NSSet ... 之類的對象)梳玫,還是非集合類對象(NSString, NSNumber ... 之類的對象)爹梁,接收到copy和mutableCopy消息時右犹,都遵循以下準則:
1\. copy 返回的是不可變對象(immutableObject);如果用copy返回值調(diào)用mutable對象的方法就會crash姚垃。
2\. mutableCopy 返回的是可變對象(mutableObject)念链。
一、非集合類對象的copy與mutableCopy
在非集合類對象中积糯,對不可變對象進行copy操作掂墓,是指針復(fù)制,mutableCopy操作是內(nèi)容復(fù)制看成;
對可變對象進行copy和mutableCopy都是內(nèi)容復(fù)制君编。用代碼簡單表示如下:
NSString *str = @"hello word!";
NSString *strCopy = [str copy] // 指針復(fù)制,strCopy與str的地址一樣
NSMutableString *strMCopy = [str mutableCopy] // 內(nèi)容復(fù)制川慌,strMCopy與str的地址不一樣
NSMutableString *mutableStr = [NSMutableString stringWithString: @"hello word!"];
NSString *strCopy = [mutableStr copy] // 內(nèi)容復(fù)制
NSMutableString *strMCopy = [mutableStr mutableCopy] // 內(nèi)容復(fù)制
二吃嘿、集合類對象的copy與mutableCopy (同上)
在集合類對象中,對不可變對象進行copy操作梦重,是指針復(fù)制兑燥,mutableCopy操作是內(nèi)容復(fù)制;
對可變對象進行copy和mutableCopy都是內(nèi)容復(fù)制琴拧。但是:集合對象的內(nèi)容復(fù)制僅限于對象本身降瞳,對集合內(nèi)的對象元素仍然是指針復(fù)制。(即單層內(nèi)容復(fù)制)
NSArray *arr = @[@[@"a", @"b"], @[@"c", @"d"];
NSArray *copyArr = [arr copy]; // 指針復(fù)制
NSMutableArray *mCopyArr = [arr mutableCopy]; //單層內(nèi)容復(fù)制
NSMutableArray *array = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"a"],@"b",@"c",nil];
NSArray *copyArr = [mutableArr copy]; // 單層內(nèi)容復(fù)制
NSMutableArray *mCopyArr = [mutableArr mutableCopy]; // 單層內(nèi)容復(fù)制
【總結(jié)一句話】:
只有對不可變對象進行copy操作是指針復(fù)制(淺復(fù)制)蚓胸,其它情況都是內(nèi)容復(fù)制(深復(fù)制)挣饥!
- *這個寫法會出什么問題:@property (nonatomic, copy) NSMutableArray arr;
問題:添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因為找不到對應(yīng)的方法而崩潰。
//如:-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x7fcd1bc30460
// copy后返回的是不可變對象(即 arr 是 NSArray 類型沛膳,NSArray 類型對象不能調(diào)用 NSMutableArray 類型對象的方法)
原因:是因為 copy 就是復(fù)制一個不可變 NSArray 的對象亮靴,不能對 NSArray 對象進行添加/修改。
如何讓自己的類用 copy 修飾符于置?如何重寫帶 copy 關(guān)鍵字的 setter茧吊?
若想令自己所寫的對象具有拷貝功能,則需實現(xiàn) NSCopying 協(xié)議八毯。如果自定義的對象分為可變版本與不可變版本搓侄,那么就要同時實現(xiàn) NSCopying 與 NSMutableCopying 協(xié)議。
具體步驟:
1. 需聲明該類遵從 NSCopying 協(xié)議
2. 實現(xiàn) NSCopying 協(xié)議的方法话速。
// 該協(xié)議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;
// 注意:使用 copy 修飾符讶踪,調(diào)用的是copy方法,其實真正需要實現(xiàn)的是 “copyWithZone” 方法泊交。**寫一個 setter 方法用于完成 @property (nonatomic, retain) NSString name乳讥,寫一個 setter 方法用于完成 @property (nonatomic, copy) NSString name
答:
// retain
- (void)setName:(NSString *)str {
[str retain];
[_name release];
_name = str;
}
// copy
- (void)setName:(NSString *)str {
id t = [str copy];
[_name release];
_name = t;
}
@synthesize 和 @dynamic 分別有什么作用柱查?
@property有兩個對應(yīng)的詞,一個是@synthesize(合成實例變量)云石,一個是@dynamic唉工。
如果@synthesize和@dynamic都沒有寫,那么默認的就是 @synthesize var = _var;
// 在類的實現(xiàn)代碼里通過 @synthesize 語法可以來指定實例變量的名字汹忠。(@synthesize var = _newVar;)
1. @synthesize 的語義是如果你沒有手動實現(xiàn)setter方法和getter方法淋硝,那么編譯器會自動為你加上這兩個方法。
2. @dynamic 告訴編譯器宽菜,屬性的setter與getter方法由用戶自己實現(xiàn)谣膳,不自動生成(如,@dynamic var)铅乡。常見的 Objective-C 的數(shù)據(jù)類型有那些继谚,和C的基本數(shù)據(jù)類型有什么區(qū)別?如:NSInteger和int
答:
Objective-C的數(shù)據(jù)類型有NSString阵幸,NSNumber花履,NSArray,NSMutableArray侨嘀,NSData等等臭挽,這些都是class,創(chuàng)建后便是對象咬腕,而C語言的基本數(shù)據(jù)類型int欢峰,只是一定字節(jié)的內(nèi)存空間,用于存放數(shù)值;NSInteger是基本數(shù)據(jù)類型涨共,并不是NSNumber的子類纽帖,當然也不是NSObject的子類。NSInteger是基本數(shù)據(jù)類型Int或者Long的別名(NSInteger的定義typedef long NSInteger)举反,它的區(qū)別在于懊直,NSInteger會根據(jù)系統(tǒng)是32位還是64位來決定是本身是int還是long。id 聲明的對象有什么特性火鼻?
答:id 聲明的對象具有運行時的特性室囊,即可以指向任意類型的Objcetive-C的對象。
- Objective-C 如何對內(nèi)存管理的魁索,說說你的看法和解決方法融撞?
答:Objective-C的內(nèi)存管理主要有三種方式ARC(自動內(nèi)存計數(shù))、手動內(nèi)存計數(shù)粗蔚、內(nèi)存池尝偎。
1). 自動內(nèi)存計數(shù)ARC:由Xcode自動在App編譯階段,在代碼中添加內(nèi)存管理代碼。
2). 手動內(nèi)存計數(shù)MRC:遵循內(nèi)存誰申請致扯、誰釋放肤寝;誰添加,誰釋放的原則抖僵。
3). 內(nèi)存釋放池Release Pool:把需要釋放的內(nèi)存統(tǒng)一放在一個池子中鲤看,當池子被抽干后(drain),池子中所有的內(nèi)存空間也被自動釋放掉裆针。內(nèi)存池的釋放操作分為自動和手動刨摩。自動釋放受runloop機制影響寺晌。
Objective-C 中創(chuàng)建線程的方法是什么世吨?如果在主線程中執(zhí)行代碼,方法是什么呻征?如果想延時執(zhí)行代碼耘婚、方法又是什么?
答:線程創(chuàng)建有三種方法:使用NSThread創(chuàng)建陆赋、使用GCD的dispatch沐祷、使用子類化的NSOperation,然后將其加入NSOperationQueue;在主線程執(zhí)行代碼,方法是performSelectorOnMainThread攒岛,如果想延時執(zhí)行代碼可以用performSelector:onThread:withObject:waitUntilDone:Category(類別)赖临、 Extension(擴展)和繼承的區(qū)別
區(qū)別:
1. 分類有名字,類擴展沒有分類名字灾锯,是一種特殊的分類兢榨。
2. 分類只能擴展方法(屬性僅僅是聲明,并沒真正實現(xiàn))顺饮,類擴展可以擴展屬性吵聪、成員變量和方法。
3. 繼承可以增加兼雄,修改或者刪除方法吟逝,并且可以增加屬性。
- 我們說的OC是動態(tài)運行時語言是什么意思赦肋?
答:主要是將數(shù)據(jù)類型的確定由編譯時块攒,推遲到了運行時。簡單來說, 運行時機制使我們直到運行時才去決定一個對象的類別,以及調(diào)用該類別對象指定方法佃乘。
為什么我們常見的delegate屬性都用是week而不是retain/strong囱井?
答:是為了防止delegate兩端產(chǎn)生不必要的循環(huán)引用。
@property (nonatomic, weak) id<UITableViewDelegate> delegate;什么時候用delete恕稠,什么時候用Notification琅绅?
Delegate(委托模式):1對1的反向消息通知功能。
Notification(通知模式):只想要把消息發(fā)送出去鹅巍,告知某些狀態(tài)的變化千扶。但是并不關(guān)心誰想要知道這個料祠。什么是 KVO 和 KVC?
KVC(Key-Value-Coding):鍵值編碼 是一種通過字符串間接訪問對象的方式(即給屬性賦值)
舉例說明:
stu.name = @"張三" // 點語法給屬性賦值
[stu setValue:@"張三" forKey:@"name"]; // 通過字符串使用KVC方式給屬性賦值
stu1.nameLabel.text = @"張三";
[stu1 setValue:@"張三" forKey:@"nameLabel.text"]; // 跨層賦值
2). KVO(key-Value-Observing):鍵值觀察機制 他提供了觀察某一屬性變化的方法澎羞,極大的簡化了代碼髓绽。
KVO只能被KVC觸發(fā),包括使用setValue:forKey:方法和點語法妆绞。
// 通過下方方法為屬性添加KVO觀察
- (void)addObserver:(NSObject *)observer
forKeyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
context:(nullable void *)context;
// 當被觀察的屬性發(fā)送變化時顺呕,會自動觸發(fā)下方方法
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context{}
KVC 和 KVO 的 keyPath 可以是屬性、實例變量括饶、成員變量株茶。
KVC的底層實現(xiàn)?
當一個對象調(diào)用setValue方法時图焰,方法內(nèi)部會做以下操作:
1). 檢查是否存在相應(yīng)的key的set方法启盛,如果存在,就調(diào)用set方法技羔。
2). 如果set方法不存在僵闯,就會查找與key相同名稱并且?guī)聞澗€的成員變量,如果有藤滥,則直接給成員變量屬性賦值鳖粟。
3). 如果沒有找到_key,就會查找相同名稱的屬性key拙绊,如果有就直接賦值向图。
4). 如果還沒有找到,則調(diào)用valueForUndefinedKey:和setValue:forUndefinedKey:方法时呀。
這些方法的默認實現(xiàn)都是拋出異常张漂,我們可以根據(jù)需要重寫它們豆拨。KVO的底層實現(xiàn)待讳?
KVO基于runtime機制實現(xiàn)萤衰。
ViewController生命周期
按照執(zhí)行順序排列:
1. initWithCoder:通過nib文件初始化時觸發(fā)携狭。
2. awakeFromNib:nib文件被加載的時候法褥,會發(fā)生一個awakeFromNib的消息到nib文件中的每個對象酣藻。
3. loadView:開始加載視圖控制器自帶的view锭沟。
4. viewDidLoad:視圖控制器的view被加載完成尔当。
5. viewWillAppear:視圖控制器的view將要顯示在window上坞靶。
6. updateViewConstraints:視圖控制器的view開始更新AutoLayout約束憔狞。
7. viewWillLayoutSubviews:視圖控制器的view將要更新內(nèi)容視圖的位置。
8. viewDidLayoutSubviews:視圖控制器的view已經(jīng)更新視圖的位置彰阴。
9. viewDidAppear:視圖控制器的view已經(jīng)展示到window上瘾敢。
10. viewWillDisappear:視圖控制器的view將要從window上消失。
11. viewDidDisappear:視圖控制器的view已經(jīng)從window上消失。方法和選擇器有何不同簇抵?
selector是一個方法的名字庆杜,方法是一個組合體,包含了名字和實現(xiàn)碟摆。你是否接觸過OC中的反射機制晃财?簡單聊一下概念和使用
1). class反射
通過類名的字符串形式實例化對象。
Class class = NSClassFromString(@"student");
Student *stu = [[class alloc] init];
將類名變?yōu)樽址? Class class =[Student class];
NSString *className = NSStringFromClass(class);
2). SEL的反射
通過方法的字符串形式實例化方法典蜕。
SEL selector = NSSelectorFromString(@"setName");
[stu performSelector:selector withObject:@"Mike"];
將方法變成字符串断盛。
NSStringFromSelector(@selector*(setName:));
- 調(diào)用方法有兩種方式:
利用performSelector 和NSInvocation來調(diào)用
相同點:父類都是NSObject不同點:performSelector最多傳兩個參數(shù),使用比較簡單
###### performSelector的方法以及部分使用方法
```
- (id)performSelector:(SEL)aSelector;- (id)performSelector:(SEL)aSelector withObject:(id)object;- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
```
```
if ([self canPerformAction:@selector(myLog:) withSender:nil]) { [self performSelector:@selector(p_Log:) withObject:@"abc" afterDelay:5];} - (void)p_Log:(NSString*)log{ NSLog(@"MyLog = %@",log); }
```
###### NSInvocation使用方法
```
NSString *str1 = @"a"; NSString *str2 = @"b"; NSString *str3 = @"c"; NSMethodSignature *sign = [self methodSignatureForSelector:@selector(personInfo:age:gender:)]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sign]; [invocation setTarget:self]; [invocation setSelector:@selector(personInfo:age:gender:)]; [invocation setArgument:&str1 atIndex:2]; [invocation setArgument:&str2 atIndex:3]; [invocation setArgument:&str3 atIndex:4]; [invocation invoke]; - (void)personInfo:(NSString *)strName age:(NSString *)strAge gender:(NSString *)strGender { NSLog(@"%@,%@,%@",strName,strAge,strGender);}
```
一開始以為setArgument 的index 從0開始代表第一個參數(shù)愉舔,結(jié)果崩潰了钢猛,po了一下發(fā)現(xiàn)
```
Printing description of invocation:<NSInvocation: 0x17027a500>return value: {v} voidtarget: {@} 0x0selector: {:} nullargument 2: {@} 0x0argument 3: {@} 0x0argument 4: {@} 0x0
```
```
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;
```
從這個方法可以看的出來的types是char類型的 personInfo這個方法其實就是"v@:@@@"。
這里的Index要從2開始屑宠,以為0跟1已經(jīng)被占據(jù)了厢洞,分別是self(target),selector(_cmd)
- 如何對iOS設(shè)備進行性能測試仇让?
1.app使用過程中典奉,接聽電話∩ミ矗可以測試不同的通話時間的長短卫玖,對于通話結(jié)束后,原先打開的app的響應(yīng)踊淳,比如是否停留在原先界面假瞬,繼續(xù)操作時的相應(yīng)速度等。
2.app使用過程中迂尝,有推送消息時脱茉,對app的使用影響
3.設(shè)備在充電時,app的響應(yīng)以及操作流暢度
4.設(shè)備在不同電量時(低于10%垄开,50%琴许,95%),app的響應(yīng)以及操作流暢度
5.意外斷電時溉躲,app數(shù)據(jù)丟失情況
6.網(wǎng)絡(luò)環(huán)境變化時榜田,app的應(yīng)對情況如何:是否有適當提示?從有網(wǎng)絡(luò)環(huán)境到無網(wǎng)絡(luò)環(huán)境時,app的反饋如何?從無網(wǎng)絡(luò)環(huán)境回到有網(wǎng)絡(luò)環(huán)境時锻梳,是否能自動加載數(shù)據(jù)箭券,多久才能開始加載數(shù)據(jù)
7.多點觸摸的情況
8.跟其他app之間互相切換時的響應(yīng)
9.進程關(guān)閉再重新打開的反饋
10.IOS系統(tǒng)語言環(huán)境變化時
- 開發(fā)項目時你是怎么檢查內(nèi)存泄露?
一: 內(nèi)存泄漏
內(nèi)存泄漏是編程中常常見到的一個問題疑枯,內(nèi)存泄漏往往會一種奇怪的方式來表現(xiàn)出來,基本上每個程序都表現(xiàn)出不同的方式辩块。 但是一般最后的結(jié)果只有兩個,一個是程序當?shù)簦粋€是系統(tǒng)內(nèi)存不足废亭。 還有一種就是比較介于中間的結(jié)果程序不會當古今,但是系統(tǒng)的反映時間明顯降低,需要定時的Reboot才會正常滔以。
有一個很簡單的辦法來檢查一個程序是否有內(nèi)存泄漏捉腥。就是是用Windows的任務(wù)管理器(Task Manager)。運行程序你画,然后在任務(wù)管理器里面查看 “內(nèi)存使用”和”虛擬內(nèi)存大小”兩項抵碟,當程序請求了它所需要的內(nèi)存之后,如果虛擬內(nèi)存還是持續(xù)的增長的話坏匪,就說明了這個程序有內(nèi)存泄漏問題拟逮。 當然如果內(nèi)存泄漏的數(shù)目非常的小,用這種方法可能要過很長時間才能看的出來适滓。
當然最簡單的辦法大概就是用CompuWare的BoundChecker 之類的工具來檢測了敦迄,不過這些工具的價格對于個人來講稍微有點奢侈了。
如果是已經(jīng)發(fā)布的程序凭迹,檢查是否有內(nèi)存泄漏是又費時又費力罚屋。所以內(nèi)存泄漏應(yīng)該在Code的生成過程就要時刻進行檢查。
二: 原因
內(nèi)存泄漏產(chǎn)生的原因一般是三種情況:
1. 分配完內(nèi)存之后忘了回收嗅绸;
2. 程序Code有問題脾猛,造成沒有辦法回收;
3. 某些API函數(shù)操作不正確鱼鸠,造成內(nèi)存泄漏猛拴。
1.內(nèi)存忘記回收,這個是不應(yīng)該的事情蚀狰。但是也是在代碼種很常見的問題愉昆。分配內(nèi)存之后,用完之后麻蹋,就一定要回收跛溉。如果不回收,那就造成了內(nèi)存的泄漏哥蔚,造成內(nèi)存泄漏的Code如果被經(jīng)常調(diào)用的話倒谷,那內(nèi)存泄漏的數(shù)目就會越來越多的。從而影響整個系統(tǒng)的運行糙箍。比如下面的代碼:
for (int =0;I<100;I++)
{
Temp = new BYTE[100];
}
就會產(chǎn)生 100*100Byte的內(nèi)存泄漏渤愁。
2. 在某些時候,因為代碼上寫的有問題深夯,會導(dǎo)致某些內(nèi)存想回收都收不回來抖格,比如下面的代碼:
Temp1 = new BYTE[100];
Temp2 = new BYTE[100];
Temp2 = Temp1;
這樣诺苹,Temp2的內(nèi)存地址就丟掉了,而且永遠都找不回了雹拄,這個時候Temp2的內(nèi)存空間想回收都沒有辦法收奔。
3\. API函數(shù)應(yīng)用不當,在Windows提供API函數(shù)里面有一些特殊的API滓玖,比如FormatMessage坪哄。 如果你給它參數(shù)中有FORMAT_MESSAGE_ALLOCATE_BUFFER,它會在函數(shù)內(nèi)部New一塊內(nèi)存Buffer出來势篡。但是這個 buffer需要你調(diào)用LocalFree來釋放翩肌。 如果你忘了,那就會產(chǎn)生內(nèi)存泄漏禁悠。
三: 檢查方法
一般的內(nèi)存泄漏檢查的確是很困難念祭,但是也不是完全沒有辦法。如果你用VC的庫來寫東西的話碍侦,那么很幸運的是粱坤,你已經(jīng)有了很多檢查內(nèi)存泄漏的工具,只是你想不想用的問題了瓷产。Visual C++的Debug版本的C運行庫(C Runtime Library)站玄。它已經(jīng)提供好些函數(shù)來幫助你診斷你的代碼和跟蹤內(nèi)存泄漏。 而且最方便的地方是這些函數(shù)在Release版本中完全不起任何作用拦英,這樣就不會影響你的Release版本程序的運行效率蜒什。
比如下面的例子里面,有一個明細的內(nèi)存泄漏疤估。當然如果只有這么幾行代碼的話,是很容易看出有內(nèi)存泄漏的霎冯。但是想在成千上萬行代碼里面檢查內(nèi)存泄漏問題就不是那么容易了铃拇。
char * pstr = new char[5];
lstrcpy(pstr,"Memory leak");
如果我們在Debug版本的Code里面對堆(Heap)進行了操作,包括malloc沈撞, free慷荔, calloc, realloc缠俺, new 和 delete可以利用VC Debug運行時庫中堆Debug函數(shù)來做堆的完整性和安全性檢查显晶。比如上面的代碼,lstrcpy的操作明顯破壞了pstr的堆結(jié)構(gòu)壹士。使其溢出磷雇,并破壞了臨近的數(shù)據(jù)。那我們可以在調(diào)用lstrcpy之后的代碼里面加入 _CrtCheckMemory函數(shù)躏救。_CrtCheckMemory函數(shù)發(fā)現(xiàn)前面的lstrcpy使得pstr的堆結(jié)構(gòu)被破壞唯笙,會輸出這樣的報告:
emory check error at 0x00372FA5 = 0x79, should be 0xFD.
memory check error at 0x00372FA6 = 0x20, should be 0xFD.
memory check error at 0x00372FA7 = 0x6C, should be 0xFD.
memory check error at 0x00372FA8 = 0x65, should be 0xFD.
DAMAGE: after Normal block (#41) at 0x00372FA0.
Normal located at 0x00372FA0 is 5 bytes long.
它告訴說 pstr的長度應(yīng)該時5個Bytes螟蒸,但是在5Bytes后面的幾個Bytes也被非法改寫了。提醒你產(chǎn)生了越界操作崩掘。_CrtCheckMemory 的返回值只有TRUE和FALSE七嫌,那么你可以用_ASSERTE()來報告出錯信息。 上面的語句可以換成 _ASSERTE(_CrtCheckMemory())苞慢; 這樣Debug版本的程序在運行的時候就會彈出一個警告對話框诵原,這樣就不用在運行時候一直盯著Output窗口看了。這個時候按Retry挽放,就可以進入源代碼調(diào)試了皮假。看看問題到底出在哪里骂维。
其他類似的函數(shù)還有 _CrtDbgReport, _CrtDoForAllClientObjects, _CrtDumpMemoryLeaks,_CrtIsValidHeapPointer, _CrtIsMemoryBlock, _CrtIsValidPointer,_CrtMemCheckpoint, _CrtMemDifference, _CrtMemDumpAllObjectsSince, _CrtMemDumpStatistics, _CrtSetAllocHook, _CrtSetBreakAlloc, _CrtSetDbgFlag,_CrtSetDumpClient, _CrtSetReportFile, _CrtSetReportHook, _CrtSetReportMode
這些函數(shù)全部都可以用來在Debug版本中檢查內(nèi)存的使用情況惹资。具體怎么使用這些函數(shù)就不在這里說明了,各位可以去查查MSDN航闺。在這些函數(shù)中用處比較大的褪测,或者說使用率會比較高的函數(shù)是_CrtMemCheckpoint, 設(shè)置一個內(nèi)存檢查點潦刃。這個函數(shù)會取得當前內(nèi)存的運行狀態(tài)侮措。 _CrtMemDifference 檢查兩種內(nèi)存狀態(tài)的異同。 _CrtMemDumpAllObjectsSince 從程序運行開始乖杠,或者從某個內(nèi)存檢查點開始Dump出堆中對象的信息分扎。還有就是_CrtDumpMemoryLeaks當發(fā)生內(nèi)存溢出的時候Dump出堆中的內(nèi)存信息。 _CrtDumpMemoryLeaks一般都在有懷疑是內(nèi)存泄漏的代碼后面調(diào)用胧洒。比如下面的例子:
#include <windows.h>
#include <crtdbg.h>
void main()
{
char * pstr;
pstr = new char[5];
_CrtDumpMemoryLeaks();
}
輸出: Detected memory leaks! à提醒你,代碼有內(nèi)存泄漏.
Dumping objects ->
{44} normal block at 0x00372DB8, 5 bytes long.
Data: < > CD CD CD CD CD
Object dump complete.
如果你雙擊包含行文件名的輸出行畏吓,指針將會跳到源文件中內(nèi)存被分配地方的行。當無法確定那些代碼產(chǎn)生了內(nèi)存泄漏的時候卫漫,我們就需要進行內(nèi)存狀態(tài)比較菲饼。在可疑的代碼段的前后設(shè)置內(nèi)存檢查點,比較內(nèi)存使用是否有可疑的變化列赎。以確定內(nèi)存是否有泄漏宏悦。為此要先定義三個_CrtMemState 對象來保存要比較的內(nèi)存狀態(tài)。兩個是用來比較包吝,一個用了保存前面兩個之間的區(qū)別饼煞。
_CrtMemState Sh1,Sh2,Sh_Diff;
char *pstr1 = new char[100];
_CrtMemCheckPoint(&Sh1); ->設(shè)置第一個內(nèi)存檢查點
char *pstr2 = new char[100];
_CrtMemCheckPoint(&Sh2); ->設(shè)置第二個內(nèi)存檢查點
_CrtMemDifference(&Sh_Diff, &Sh1, &Sh2); ->檢查變化
_CrtMemDumpAllObjectsSince(&Sh_Diff); ->Dump變化
如果你的程序中使用了MFC類庫,那么內(nèi)存泄漏的檢查方法就相當?shù)暮唵瘟耸健R驗镈ebug版本的MFC本身就提供一部分的內(nèi)存泄漏檢查砖瞧。 大部分的new 和delete沒有配對使用而產(chǎn)生的內(nèi)存泄漏,MFC都會產(chǎn)生報告掺喻。這個主要是因為MFC重載了Debug版本的new 和delete操作符芭届, 并且對前面提到的API函數(shù)重新進行了包裝储矩。在MFC類庫中檢查內(nèi)存泄漏的Class就叫 CMemoryState,它重新包裝了了_CrtMemState褂乍,_CrtMemCheckPoint持隧, _CrtMemDifference, _CrtMemDumpAllObjectsSince這些函數(shù)逃片。并對于其他的函數(shù)提供了Afx開頭的函數(shù)屡拨,供MFC程序使用。比如 AfxCheckMemory褥实, AfxDumpMemoryLeaks 這些函數(shù)的基本用法同上面提到的差不多呀狼。 CMemoryState和相關(guān)的函數(shù)的定義都在Afx.h這個頭文件中。 有個簡單的辦法可以跟蹤到這些函數(shù)的聲明损离。在VC中找到MFC程序代碼中下面的代碼哥艇, 一般都在X.cpp的開頭部分
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
把光標移到DEBUG_NEW上面 按F12,就可以進入Afx.h中定義這些Class和函數(shù)的代碼部分僻澎。 VC中內(nèi)存泄漏的常規(guī)檢查辦法主要是上面的兩種貌踏。當然這兩種方法只是針對于Debug版本的Heap的檢查。如果Release版本中還有內(nèi)存泄漏窟勃,那么檢查起來就麻煩很多了祖乳。
4 .總結(jié):
實際上Heap的內(nèi)存泄漏問題是相當?shù)暮貌榈摹C的提供的檢查工具也不太少秉氧,但是如果是棧出了什么問題眷昆,恐怕就麻煩很多了。棧出問題汁咏,一般不會產(chǎn)生內(nèi)存泄漏亚斋,但是你的代碼的邏輯上很有可能會有影響。這個是最最痛苦的事情梆暖。 編程伞访,就是小心,小心再小心而已轰驳。
- 什么是懶加載?
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">答:懶加載就是只在用到的時候才去初始化弟灼。也可以理解成延時加載级解。
我覺得最好也最簡單的一個例子就是tableView中圖片的加載顯示了, 一個延時加載, 避免內(nèi)存過高,一個異步加載,避免線程堵塞提高用戶體驗。
- 類變量的 @public田绑,@protected勤哗,@private,@package 聲明各有什么含義掩驱?
@public 任何地方都能訪問;
@protected 該類和子類中訪問,是默認的;
@private 只能在本類中訪問;
@package 本包內(nèi)使用,跨包不可以芒划。
- 什么是謂詞冬竟?
謂詞就是通過NSPredicate給定的邏輯條件作為約束條件,完成對數(shù)據(jù)的篩選。
//定義謂詞對象,謂詞對象中包含了過濾條件(過濾條件比較多)
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"age<%d",30];
//使用謂詞條件過濾數(shù)組中的元素,過濾之后返回查詢的結(jié)果
NSArray *array = [persons filteredArrayUsingPredicate:predicate];
- isa指針問題
isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念?而Class里也有個isa的指針, 指向meteClass(元類)民逼。元類保存了類方法的列表泵殴。當類方法被調(diào) 用時,先會從本身查找類方法的實現(xiàn),如果沒有,元類會向他父類查找該方法。同時注意的是:元類(meteClass)也是類,它也是對象拼苍。元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass)笑诅。根元類的isa指針指向本身,這樣形成了一個封閉的內(nèi)循環(huán)。
- 如何訪問并修改一個類的私有屬性疮鲫?
1.KVC
我們可以用setValue:的方法設(shè)置私有屬性吆你,并利用valueForKey:的方法訪問私有屬性。假設(shè)我們有一個類Person俊犯,并且這個類有一個私有屬性name妇多。看代碼:
// 利用KVC訪問私有屬性
Person * ls = [[Person alloc] init];
[ls setValue:@"wo" forKey:@"name"];
NSLog(@"=======%@", [ls valueForKey:@"name"])
2.runtime
我們可以利用runtime獲取某個類的所有屬性(私有屬性燕侠、非私有屬性)者祖,在獲取到某個類的屬性后就可以對該屬性進行訪問以及修改了。之前有篇博客就是通過runtime獲取某個類的所有成員變量名稱然后對其進行歸檔贬循,博客地址:[http://blog.csdn.net/u010105969/article/details/62233752](http://blog.csdn.net/u010105969/article/details/62233752)咸包。看代碼:
// 利用run time訪問并修改私有屬性
Person *p = [Person new];
// IVar是runtime聲明的一個宏
unsigned int count = 0; //count記錄變量的數(shù)量
// 獲取類的所有屬性變量
Ivar *members = class_copyIvarList([Person class], &count);
for (int i = 0; i < count; i++) {
Ivar ivar = members[i];
// 取得屬性名并轉(zhuǎn)成字符串類型
const char *memberName = ivar_getName(ivar);
NSLog(@"%s",memberName);
Ivar name = members[0];
// 修改屬性值
object_setIvar(p, name, @"bushiwo");
}
NSLog(@"%@", [p valueForKey:@"name"]);
- 一個objc對象的isa的指針指向什么杖虾?有什么作用烂瘫?
isa 指的就是 是個什么,對象的isa指向類奇适,類的isa指向元類(meta class)坟比,元類isa指向元類的根類。isa幫助一個對象找到它的方法嚷往。isa:是一個Class 類型的指針. 每個實例對象有個isa的指針,他指向?qū)ο蟮念惛鹫耍鳦lass里也有個isa的指針, 指向meteClass(元類)。元類保存了類方法的列表皮仁。當類方法被調(diào)用時籍琳,先會從本身查找類方法的實現(xiàn),如果沒有贷祈,元類會向他父類查找該方法趋急。同時注意的是:**元類(meteClass)也是類,它也是對象势誊。**元類也有isa指針,它的isa指針最終指向的是一個根元類(root meteClass).根元類的isa指針指向本身呜达,這樣形成了一個封閉的內(nèi)循環(huán)∷诔埽可以看看這位大神寫的文章[http://blog.csdn.net/kesalin/article/details/7211228](https://link.jianshu.com/?t=http://blog.csdn.net/kesalin/article/details/7211228)
- 下面的代碼輸出什么?
@implementation Son : Father
- (id)init {
if (self = [super init]) {
NSLog(@"%@", NSStringFromClass([self class])); // Son
NSLog(@"%@", NSStringFromClass([super class])); // Son
}
return self;
}
@end
// 解析:
self 是類的隱藏參數(shù),指向當前調(diào)用方法的這個類的實例抱慌。
super是一個Magic Keyword,它本質(zhì)是一個編譯器標示符谈喳,和self是指向的同一個消息接收者。
不同的是:super會告訴編譯器侥祭,調(diào)用class這個方法時叁执,要去父類的方法,而不是本類里的矮冬。
上面的例子不管調(diào)用[self class]還是[super class]谈宛,接受消息的對象都是當前 Son *obj 這個對象。
- 寫一個完整的代理胎署,包括聲明吆录、實現(xiàn)
// 創(chuàng)建
@protocol MyDelagate
@required
-(void)eat:(NSString *)foodName;
@optional
-(void)run;
@end
// 聲明 .h
@interface person: NSObject<MyDelagate>
@end
// 實現(xiàn) .m
@implementation person
- (void)eat:(NSString *)foodName {
NSLog(@"吃:%@!", foodName);
}
- (void)run {
NSLog(@"run!");
}
@end
- isKindOfClass、isMemberOfClass琼牧、selector作用分別是什么
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">isKindOfClass:作用是某個對象屬于某個類型或者繼承自某類型恢筝。
isMemberOfClass:某個對象確切屬于某個類型。
selector:通過方法名巨坊,獲取在內(nèi)存中的函數(shù)的入口地址撬槽。
- delegate 和 notification 的區(qū)別
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">1). 二者都用于傳遞消息,不同之處主要在于一個是一對一的趾撵,另一個是一對多的侄柔。
2). notification通過維護一個array,實現(xiàn)一對多消息的轉(zhuǎn)發(fā)占调。
3). delegate需要兩者之間必須建立聯(lián)系暂题,不然沒法調(diào)用代理的方法;notification不需要兩者之間有聯(lián)系究珊。
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">閉包(block):閉包就是獲取其它函數(shù)局部變量的匿名函數(shù)薪者。
- block反向傳值
在控制器間傳值可以使用代理或者block,使用block相對來說簡潔剿涮。
-
在前一個控制器的touchesBegan:方法內(nèi)實現(xiàn)如下代碼言津。
// OneViewController.m
TwoViewController *twoVC = [[TwoViewController alloc] init];
twoVC.valueBlcok = ^(NSString *str) {
NSLog(@"OneViewController拿到值:%@", str);
};
[self presentViewController:twoVC animated:YES completion:nil];// TwoViewController.h (在.h文件中聲明一個block屬性)
@property (nonatomic ,strong) void(^valueBlcok)(NSString *str);// TwoViewController.m (在.m文件中實現(xiàn)方法)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 傳值:調(diào)用block
if (_valueBlcok) {
_valueBlcok(@"123456");
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
- block的注意點
1). 在block內(nèi)部使用外部指針且會造成循環(huán)引用情況下,需要用__week修飾外部指針:
__weak typeof(self) weakSelf = self;
2). 在block內(nèi)部如果調(diào)用了延時函數(shù)還使用弱指針會取不到該指針取试,因為已經(jīng)被銷毀了纺念,需要在block內(nèi)部再將弱指針重新強引用一下。
__strong typeof(self) strongSelf = weakSelf;
3). 如果需要在block內(nèi)部改變外部棧區(qū)變量的話想括,需要在用__block修飾外部變量。
- BAD_ACCESS在什么情況下出現(xiàn)烙博?
答:這種問題在開發(fā)時經(jīng)常遇到瑟蜈。原因是訪問了野指針烟逊,比如訪問已經(jīng)釋放對象的成員變量或者發(fā)消息、死循環(huán)等铺根。
- lldb(gdb)常用的控制臺調(diào)試命令宪躯?
1). p 輸出基本類型。是打印命令位迂,需要指定類型访雪。是print的簡寫
p (int)[[[self view] subviews] count]
2). po 打印對象,會調(diào)用對象description方法掂林。是print-object的簡寫
po [self view]
3). expr 可以在調(diào)試時動態(tài)執(zhí)行指定表達式臣缀,并將結(jié)果打印出來。常用于在調(diào)試過程中修改變量的值泻帮。
4). bt:打印調(diào)用堆棧精置,是thread backtrace的簡寫,加all可打印所有thread的堆棧
5). br l:是breakpoint list的簡寫
- 你一般是怎么用Instruments的锣杂?
Instruments里面工具很多脂倦,常用:
1). Time Profiler: 性能分析
2). Zombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查元莫,不智能赖阻。
3). Allocations:用來檢查內(nèi)存,寫算法的那批人也用這個來檢查踱蠢。
4). Leaks:檢查內(nèi)存火欧,看是否有內(nèi)存泄露。
- iOS中常用的數(shù)據(jù)存儲方式有哪些朽基?
數(shù)據(jù)存儲有四種方案:NSUserDefault布隔、KeyChain、file稼虎、DB衅檀。
其中File有三種方式:plist、Archive(歸檔)
DB包括:SQLite霎俩、FMDB哀军、CoreData
- iOS的沙盒目錄結(jié)構(gòu)是怎樣的?
沙盒結(jié)構(gòu):
1). Application:存放程序源文件打却,上架前經(jīng)過數(shù)字簽名杉适,上架后不可修改。
2). Documents:常用目錄柳击,iCloud備份目錄猿推,存放數(shù)據(jù)。(這里不能存緩存文件,否則上架不被通過)
3). Library:
Caches:存放體積大又不需要備份的數(shù)據(jù)蹬叭。(常用的緩存路徑)
Preference:設(shè)置目錄藕咏,iCloud會備份設(shè)置信息。
4). tmp:存放臨時文件秽五,不會被備份孽查,而且這個文件下的數(shù)據(jù)有可能隨時被清除的可能。
- iOS多線程技術(shù)有哪幾種方式坦喘?
答:pthread盲再、NSThread、GCD瓣铣、NSOperation
- GCD 與 NSOperation 的區(qū)別:
GCD 和 NSOperation 都是用于實現(xiàn)多線程:
GCD 基于C語言的底層API答朋,GCD主要與block結(jié)合使用,代碼簡潔高效坯沪。
NSOperation 屬于Objective-C類绿映,是基于GCD更高一層的封裝。復(fù)雜任務(wù)一般用NSOperation實現(xiàn)腐晾。
- 寫出使用GCD方式從子線程回到主線程的方法代碼
答:dispatch_sync(dispatch_get_main_queue(), ^{ });
- 如何用GCD同步若干個異步調(diào)用叉弦?(如根據(jù)若干個url異步加載多張圖片,然后在都下載完成后合成一張整圖)
// 使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢藻糖,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block淹冰。
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 / });
dispatch_group_async(group, queue, ^{ /加載圖片2 / });
dispatch_group_async(group, queue, ^{ /加載圖片3 */ });
// 當并發(fā)隊列組中的任務(wù)執(zhí)行完畢后才會執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
- dispatch_barrier_async(柵欄函數(shù))的作用是什么?
函數(shù)定義:dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
作用:
1.在它前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行巨柒,它后面的任務(wù)要等它執(zhí)行完成后才會開始執(zhí)行樱拴。
2.避免數(shù)據(jù)競爭
// 1.創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 2.向隊列中添加任務(wù)
dispatch_async(queue, ^{ // 1.2是并行的
NSLog(@"任務(wù)1, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2, %@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"任務(wù) barrier, %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{ // 這兩個是同時執(zhí)行的
NSLog(@"任務(wù)3, %@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4, %@",[NSThread currentThread]);
});
// 輸出結(jié)果: 任務(wù)1 任務(wù)2 ——》 任務(wù) barrier ——》任務(wù)3 任務(wù)4
// 其中的任務(wù)1與任務(wù)2,任務(wù)3與任務(wù)4 由于是并行處理先后順序不定洋满。
- 以下代碼運行結(jié)果如何晶乔?
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
});
NSLog(@"3");
}
// 只輸出:1。(主線程死鎖)
- 什么是 RunLoop
從字面上講就是運行循環(huán)牺勾,它內(nèi)部就是do-while循環(huán)正罢,在這個循環(huán)內(nèi)部不斷地處理各種任務(wù)。
一個線程對應(yīng)一個RunLoop驻民,基本作用就是保持程序的持續(xù)運行翻具,處理app中的各種事件。通過runloop回还,有事運行裆泳,沒事就休息,可以節(jié)省cpu資源柠硕,提高程序性能工禾。
主線程的run loop默認是啟動的。iOS的應(yīng)用程序里面,程序啟動后會有一個如下的main()函數(shù)
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 什么是 Runtime
Runtime又叫運行時帜篇,是一套底層的C語言API糙捺,其為iOS內(nèi)部的核心之一,我們平時編寫的OC代碼笙隙,底層都是基于它來實現(xiàn)的。
- Runtime實現(xiàn)的機制是什么坎缭,怎么用竟痰,一般用于干嘛?
1). 使用時需要導(dǎo)入的頭文件 <objc/message.h> <objc/runtime.h>
2). Runtime 運行時機制掏呼,它是一套C語言庫坏快。
3). 實際上我們編寫的所有OC代碼,最終都是轉(zhuǎn)成了runtime庫的東西憎夷。
比如:
類轉(zhuǎn)成了 Runtime 庫里面的結(jié)構(gòu)體等數(shù)據(jù)類型莽鸿,
方法轉(zhuǎn)成了 Runtime 庫里面的C語言函數(shù),
平時調(diào)方法都是轉(zhuǎn)成了 objc_msgSend 函數(shù)(所以說OC有個消息發(fā)送機制)
// OC是動態(tài)語言拾给,每個方法在運行時會被動態(tài)轉(zhuǎn)為消息發(fā)送祥得,即:objc_msgSend(receiver, selector)。
// [stu show]; 在objc動態(tài)編譯時蒋得,會被轉(zhuǎn)意為:objc_msgSend(stu, @selector(show));
4). 因此级及,可以說 Runtime 是OC的底層實現(xiàn),是OC的幕后執(zhí)行者额衙。
有了Runtime庫饮焦,能做什么事情呢?
Runtime庫里面包含了跟類窍侧、成員變量县踢、方法相關(guān)的API。
比如:
(1)獲取類里面的所有成員變量伟件。
(2)為類動態(tài)添加成員變量硼啤。
(3)動態(tài)改變類的方法實現(xiàn)。
(4)為類動態(tài)添加新的方法等锋爪。
因此丙曙,有了Runtime,想怎么改就怎么改其骄。
- 什么是 Method Swizzle(黑魔法)亏镰,什么情況下會使用?
1). 在沒有一個類的實現(xiàn)源碼的情況下拯爽,想改變其中一個方法的實現(xiàn)索抓,除了繼承它重寫、和借助類別重名方法暴力搶先之外,還有更加靈活的方法 Method Swizzle逼肯。
2). Method Swizzle 指的是改變一個已存在的選擇器對應(yīng)的實現(xiàn)的過程耸黑。OC中方法的調(diào)用能夠在運行時通過改變,通過改變類的調(diào)度表中選擇器到最終函數(shù)間的映射關(guān)系篮幢。
3). 在OC中調(diào)用一個方法大刊,其實是向一個對象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字三椿。利用OC的動態(tài)特性缺菌,可以實現(xiàn)在運行時偷換selector對應(yīng)的方法實現(xiàn)。
4). 每個類都有一個方法列表搜锰,存放著selector的名字和方法實現(xiàn)的映射關(guān)系伴郁。IMP有點類似函數(shù)指針,指向具體的方法實現(xiàn)蛋叼。
5). 我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP焊傅。
6). 我們可以利用 class_replaceMethod 來修改類。
7). 我們可以利用 method_setImplementation 來直接設(shè)置某個方法的IMP狈涮。
8). 歸根結(jié)底狐胎,都是偷換了selector的IMP。
- _objc_msgForward 函數(shù)是做什么的薯嗤,直接調(diào)用它將會發(fā)生什么顽爹?
<答:_objc_msgForward是 IMP 類型,用于消息轉(zhuǎn)發(fā)的:當向一個對象發(fā)送一條消息骆姐,但它并沒有實現(xiàn)的時候镜粤,_objc_msgForward會嘗試做消息轉(zhuǎn)發(fā)。
- 什么是 TCP / UDP ?
TCP:傳輸控制協(xié)議玻褪。
UDP:用戶數(shù)據(jù)協(xié)議肉渴。
TCP 是面向連接的,建立連接需要經(jīng)歷三次握手带射,是可靠的傳輸層協(xié)議同规。
UDP 是面向無連接的,數(shù)據(jù)傳輸是不可靠的窟社,它只管發(fā)券勺,不管收不收得到。
簡單的說灿里,TCP注重數(shù)據(jù)安全关炼,而UDP數(shù)據(jù)傳輸快點,但安全性一般匣吊。
- 通信底層原理(OSI七層模型)
OSI采用了分層的結(jié)構(gòu)化技術(shù)儒拂,共分七層:
物理層寸潦、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層社痛、傳輸層见转、會話層、表示層蒜哀、應(yīng)用層斩箫。
- 介紹一下XMPP?
XMPP是一種以XML為基礎(chǔ)的開放式實時通信協(xié)議凡怎。
簡單的說校焦,XMPP就是一種協(xié)議,一種規(guī)定统倒。就是說,在網(wǎng)絡(luò)上傳東西氛雪,XMM就是規(guī)定你上傳大小的格式房匆。
-
OC中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼报亩,方法是什么浴鸿?
// 創(chuàng)建線程的方法
- [NSThread detachNewThreadSelector:nil toTarget:nil withObject:nil]
- [self performSelectorInBackground:nil withObject:nil];
- [[NSThread alloc] initWithTarget:nil selector:nil object:nil];
- dispatch_async(dispatch_get_global_queue(0, 0), ^{});
- [[NSOperationQueue new] addOperation:nil];
// 主線程中執(zhí)行代碼的方法
- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
- tableView的重用機制?
<答:UITableView 通過重用單元格來達到節(jié)省內(nèi)存的目的: 通過為每個單元格指定一個重用標識符弦追,即指定了單元格的種類,當屏幕上的單元格滑出屏幕時岳链,系統(tǒng)會把這個單元格添加到重用隊列中,等待被重用劲件,當有新單元格從屏幕外滑入屏幕內(nèi)時掸哑,從重用隊列中找看有沒有可以重用的單元格,如果有零远,就拿過來用苗分,如果沒有就創(chuàng)建一個來使用。
- 用偽代碼寫一個線程安全的單例模式
static id _instance;
+ (id)allocWithZone:(struct _NSZone *)zone {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [super allocWithZone:zone];
});
return _instance;
}
+ (instancetype)sharedData {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
- (id)copyWithZone:(NSZone *)zone {
return _instance;
}
- 如何實現(xiàn)視圖的變形?
答:通過修改view的 transform 屬性即可牵辣。
- 在手勢對象基礎(chǔ)類UIGestureRecognizer的常用子類手勢類型中哪兩個手勢發(fā)生后摔癣,響應(yīng)只會執(zhí)行一次?
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手勢,手勢發(fā)生后,響應(yīng)只會執(zhí)行一次纬向。
- 字符串常用方法:
<pre class="md-fences mock-cm md-end-block" name="code" style="box-sizing: border-box; outline: 0px; padding: 8px; margin: 0px; position: relative; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-family: Consolas, Inconsolata, Courier, monospace; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); word-break: break-all;">NSString *str = @"abc*123";
NSArray *arr = [str componentsSeparatedByString:@"*"]; //以目標字符串把原字符串分割成兩部分择浊,存到數(shù)組中。@[@"abc", @"123"];
- 如何高性能的給 UIImageView 加個圓角?
* 不好的解決方案:使用下面的方式會`強制Core Animation提前渲染屏幕的離屏繪制, 而離屏繪制就會給性能帶來負面影響`逾条,會有卡頓的現(xiàn)象出現(xiàn)琢岩。
self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;
* 正確的解決方案:使用繪圖技術(shù)
- (UIImage *)circleImage {
// NO代表透明
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
// 獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加一個圓
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGContextAddEllipseInRect(ctx, rect);
// 裁剪
CGContextClip(ctx);
// 將圖片畫上去
[self drawInRect:rect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();
return image;
}
* 還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 添加了的圓角,其實也是通過繪圖技術(shù)來實現(xiàn)的膳帕。
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
imageView.center = CGPointMake(200, 300);
UIImage *anotherImage = [UIImage imageNamed:@"image"];
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds
cornerRadius:50] addClip];
[anotherImage drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
- 你是怎么封裝一個view的
1). 可以通過純代碼或者xib的方式來封裝子控件
2). 建立一個跟view相關(guān)的模型粘捎,然后將模型數(shù)據(jù)傳給view薇缅,通過模型上的數(shù)據(jù)給view的子控件賦值
/**
* 純代碼初始化控件時一定會走這個方法
*/
- (instancetype)initWithFrame:(CGRect)frame {
if(self = [super initWithFrame:frame]) {
[self setupUI];
}
return self;
}
/**
* 通過xib初始化控件時一定會走這個方法
*/
- (id)initWithCoder:(NSCoder *)aDecoder {
if(self = [super initWithCoder:aDecoder]) {
[self setupUI];
}
return self;
}
- (void)setupUI {
// 初始化代碼
}
- HTTP協(xié)議中 POST 方法和 GET 方法有那些區(qū)別?
1\. GET用于向服務(wù)器請求數(shù)據(jù),POST用于提交數(shù)據(jù)
2\. GET請求攒磨,請求參數(shù)拼接形式暴露在地址欄泳桦,而POST請求參數(shù)則放在請求體里面,因此GET請求不適合用于驗證密碼等操作
3\. GET請求的URL有長度限制娩缰,POST請求不會有長度限制
- 請簡單的介紹下APNS發(fā)送系統(tǒng)消息的機制
APNS優(yōu)勢:杜絕了類似安卓那種為了接受通知不停在后臺喚醒程序保持長連接的行為灸撰,由iOS系統(tǒng)和APNS進行長連接替代。
APNS的原理:
1). 應(yīng)用在通知中心注冊拼坎,由iOS系統(tǒng)向APNS請求返回設(shè)備令牌(device Token)
2). 應(yīng)用程序接收到設(shè)備令牌并發(fā)送給自己的后臺服務(wù)器
3). 服務(wù)器把要推送的內(nèi)容和設(shè)備發(fā)送給APNS
4). APNS根據(jù)設(shè)備令牌找到設(shè)備浮毯,再由iOS根據(jù)APPID把推送內(nèi)容展示
?
第三方框架
-
AFNetworking 底層原理分析
AFNetworking主要是對NSURLSession和NSURLConnection(iOS9.0廢棄)的封裝,其中主要有以下類:
1). AFHTTPRequestOperationManager:內(nèi)部封裝的是 NSURLConnection, 負責(zé)發(fā)送網(wǎng)絡(luò)請求, 使用最多的一個類。(3.0廢棄)
2). AFHTTPSessionManager:內(nèi)部封裝是 NSURLSession, 負責(zé)發(fā)送網(wǎng)絡(luò)請求,使用最多的一個類泰鸡。
3). AFNetworkReachabilityManager:實時監(jiān)測網(wǎng)絡(luò)狀態(tài)的工具類债蓝。當前的網(wǎng)絡(luò)環(huán)境發(fā)生改變之后,這個工具類就可以檢測到。
4). AFSecurityPolicy:網(wǎng)絡(luò)安全的工具類, 主要是針對 HTTPS 服務(wù)盛龄。5). AFURLRequestSerialization:序列化工具類,基類饰迹。上傳的數(shù)據(jù)轉(zhuǎn)換成JSON格式
(AFJSONRequestSerializer).使用不多。
6). AFURLResponseSerialization:反序列化工具類;基類.使用比較多:
7). AFJSONResponseSerializer; JSON解析器,默認的解析器.
8). AFHTTPResponseSerializer; 萬能解析器; JSON和XML之外的數(shù)據(jù)類型,直接返回二進
制數(shù)據(jù).對服務(wù)器返回的數(shù)據(jù)不做任何處理.
9). AFXMLParserResponseSerializer; XML解析器; -
描述下SDWebImage里面給UIImageView加載圖片的邏輯
SDWebImage 中為 UIImageView 提供了一個分類UIImageView+WebCache.h, 這個分類中有一個最常用的接口sd_setImageWithURL:placeholderImage:余舶,會在真實圖片出現(xiàn)前會先顯示占位圖片啊鸭,當真實圖片被加載出來后再替換占位圖片。
加載圖片的過程大致如下:
1.首先會在 SDWebImageCache 中尋找圖片是否有對應(yīng)的緩存, 它會以url 作為數(shù)據(jù)的索引先在內(nèi)存中尋找是否有對應(yīng)的緩存
2.如果緩存未找到就會利用通過MD5處理過的key來繼續(xù)在磁盤中查詢對應(yīng)的數(shù)據(jù), 如果找到了, 就會把磁盤中的數(shù)據(jù)加載到內(nèi)存中匿值,并將圖片顯示出來
3.如果在內(nèi)存和磁盤緩存中都沒有找到赠制,就會向遠程服務(wù)器發(fā)送請求,開始下載圖片
4.下載后的圖片會加入緩存中挟憔,并寫入磁盤中
5.整個獲取圖片的過程都是在子線程中執(zhí)行钟些,獲取到圖片后回到主線程將圖片顯示出來SDWebImage原理:
調(diào)用類別的方法:
1. 從內(nèi)存(字典)中找圖片(當這個圖片在本次使用程序的過程中已經(jīng)被加載過),找到直接使用曲楚。
2. 從沙盒中找(當這個圖片在之前使用程序的過程中被加載過)厘唾,找到使用,緩存到內(nèi)存中龙誊。
3. 從網(wǎng)絡(luò)上獲取抚垃,使用,緩存到內(nèi)存趟大,緩存到沙盒鹤树。 -
友盟統(tǒng)計接口統(tǒng)計的所有功能
APP啟動速度,APP停留頁面時間等
算法
-
不用中間變量,用兩種方法交換A和B的值
// 1.中間變量
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}// 2.加法
void swap(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
}// 3.異或(相同為0逊朽,不同為1. 可以理解為不進位加法)
void swap(int a, int b) {
a = a ^ b;
b = a ^ b;
a = a ^ b;
}?
-
求最大公約數(shù)
/** 1.直接遍歷法 /
int maxCommonDivisor(int a, int b) {
int max = 0;
for (int i = 1; i <=b; i++) {
if (a % i == 0 && b % i == 0) {
max = i;
}
}
return max;
}
/* 2.輾轉(zhuǎn)相除法 */
int maxCommonDivisor(int a, int b) {
int r;
while(a % b > 0) {
r = a % b;
a = b;
b = r;
}
return b;
}// 擴展:最小公倍數(shù) = (a * b)/最大公約數(shù)
-
模擬棧操作
/**
- 棧是一種數(shù)據(jù)結(jié)構(gòu)罕伯,特點:先進后出
- 練習(xí):使用全局變量模擬棧的操作
*/
include <stdio.h>
include <stdbool.h>
include <assert.h>
//保護全局變量:在全局變量前加static后,這個全局變量就只能在本文件中使用
static int data[1024];//棧最多能保存1024個數(shù)據(jù)
static int count = 0;//目前已經(jīng)放了多少個數(shù)(相當于棧頂位置)//數(shù)據(jù)入棧 push
void push(int x){
assert(!full());//防止數(shù)組越界
data[count++] = x;
}
//數(shù)據(jù)出棧 pop
int pop(){
assert(!empty());
return data[--count];
}
//查看棧頂元素 top
int top(){
assert(!empty());
return data[count-1];
}//查詢棧滿 full
bool full() {
if(count >= 1024) {
return 1;
}
return 0;
}//查詢椷椿洌空 empty
bool empty() {
if(count <= 0) {
return 1;
}
return 0;
}int main(){
//入棧
for (int i = 1; i <= 10; i++) {
push(i);
}//出棧 while(!empty()){ printf("%d ", top()); //棧頂元素 pop(); //出棧 } printf("\n"); return 0;
}
-
排序算法
選擇排序追他、冒泡排序坟募、插入排序三種排序算法可以總結(jié)為如下:
-
都將數(shù)組分為已排序部分和未排序部分啡邑。
1. 選擇排序?qū)⒁雅判虿糠侄x在左端濒募,然后選擇未排序部分的最小元素和未排序部分的第一個元素交換。
2. 冒泡排序?qū)⒁雅判虿糠侄x在右端颁督,在遍歷未排序部分的過程執(zhí)行交換单雾,將最大元素交換到最右端赚哗。
3. 插入排序?qū)⒁雅判虿糠侄x在左端,將未排序部分元的第一個元素插入到已排序部分合適的位置硅堆。 選擇排序
-
/**
* 【選擇排序】:最值出現(xiàn)在起始端
*
* 第1趟:在n個數(shù)中找到最小(大)數(shù)與第一個數(shù)交換位置
* 第2趟:在剩下n-1個數(shù)中找到最小(大)數(shù)與第二個數(shù)交換位置
* 重復(fù)這樣的操作...依次與第三個屿储、第四個...數(shù)交換位置
* 第n-1趟,最終可實現(xiàn)數(shù)據(jù)的升序(降序)排列渐逃。
*
*/
void selectSort(int *arr, int length) {
for (int i = 0; i < length - 1; i++) { //趟數(shù)
for (int j = i + 1; j < length; j++) { //比較次數(shù)
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
* 冒泡排序
/**
* 【冒泡排序】:相鄰元素兩兩比較够掠,比較完一趟,最值出現(xiàn)在末尾
* 第1趟:依次比較相鄰的兩個數(shù)茄菊,不斷交換(小數(shù)放前祖屏,大數(shù)放后)逐個推進,最值最后出現(xiàn)在第n個元素位置
* 第2趟:依次比較相鄰的兩個數(shù)买羞,不斷交換(小數(shù)放前,大數(shù)放后)逐個推進雹食,最值最后出現(xiàn)在第n-1個元素位置
* …… ……
* 第n-1趟:依次比較相鄰的兩個數(shù)畜普,不斷交換(小數(shù)放前,大數(shù)放后)逐個推進群叶,最值最后出現(xiàn)在第2個元素位置
*/
void bublleSort(int *arr, int length) {
for(int i = 0; i < length - 1; i++) { //趟數(shù)
for(int j = 0; j < length - i - 1; j++) { //比較次數(shù)
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
-
折半查找(二分查找)
/**
- 折半查找:優(yōu)化查找時間(不用遍歷全部數(shù)據(jù))
- 折半查找的原理:
- 1> 數(shù)組必須是有序的
- 2> 必須已知min和max(知道范圍)
- 3> 動態(tài)計算mid的值吃挑,取出mid對應(yīng)的值進行比較
- 4> 如果mid對應(yīng)的值大于要查找的值,那么max要變小為mid-1
- 5> 如果mid對應(yīng)的值小于要查找的值街立,那么min要變大為mid+1
*/
// 已知一個有序數(shù)組, 和一個key, 要求從數(shù)組中找到key對應(yīng)的索引位置
int findKey(int *arr, int length, int key) {
int min = 0, max = length - 1, mid;
while (min <= max) {
mid = (min + max) / 2; //計算中間值
if (key > arr[mid]) {
min = mid + 1;
} else if (key < arr[mid]) {
max = mid - 1;
} else {
return mid;
}
}
return -1;
}?
編碼格式(優(yōu)化細節(jié))
-
在 Objective-C 中舶衬,enum 建議使用
NS_ENUM
和NS_OPTIONS
宏來定義枚舉類型。//定義一個枚舉(比較嚴密)
typedef NS_ENUM(NSInteger, BRUserGender) {
BRUserGenderUnknown, // 未知
BRUserGenderMale, // 男性
BRUserGenderFemale, // 女性
BRUserGenderNeuter // 無性
};@interface BRUser : NSObject<NSCopying>
@property (nonatomic, readonly, copy) NSString *name;
@property (nonatomic, readonly, assign) NSUInteger age;
@property (nonatomic, readonly, assign) BRUserGender gender;- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age gender:(BRUserGender)gender;
@end
//說明:
//既然該類中已經(jīng)有一個“初始化方法” 赎离,用于設(shè)置 name逛犹、age 和 gender 的初始值: 那么在設(shè)計對應(yīng) @property 時就應(yīng)該盡量使用不可變的對象:其三個屬性都應(yīng)該設(shè)為“只讀”。用初始化方法設(shè)置好屬性值之后梁剔,就不能再改變了虽画。
//屬性的參數(shù)應(yīng)該按照下面的順序排列: (原子性,讀寫荣病,內(nèi)存管理)?
-
避免使用C語言中的基本數(shù)據(jù)類型码撰,建議使用 Foundation 數(shù)據(jù)類型,對應(yīng)關(guān)系如下:
int -> NSInteger
unsigned -> NSUInteger
float -> CGFloat
動畫時間 -> NSTimeInterval?
其它知識點
HomeKit个盆,是蘋果2014年發(fā)布的智能家居平臺脖岛。
-
什么是 OpenGL朵栖、Quartz 2D?
Quatarz 2d 是Apple提供的基本圖形工具庫柴梆。只是適用于2D圖形的繪制陨溅。
OpenGL,是一個跨平臺的圖形開發(fā)庫轩性。適用于2D和3D圖形的繪制声登。 ffmpeg框架:?ffmpeg 是音視頻處理工具,既有音視頻編碼解碼功能揣苏,又可以作為播放器使用悯嗓。
-
談?wù)?UITableView 的優(yōu)化
1). 正確的復(fù)用cell。
2). 設(shè)計統(tǒng)一規(guī)格的Cell
3). 提前計算并緩存好高度(布局)卸察,因為heightForRowAtIndexPath:是調(diào)用最頻繁的方法脯厨;
4). 異步繪制,遇到復(fù)雜界面坑质,遇到性能瓶頸時合武,可能就是突破口;
4). 滑動時按需加載涡扼,這個在大量圖片展示稼跳,網(wǎng)絡(luò)加載的時候很管用!
5). 減少子視圖的層級關(guān)系
6). 盡量使所有的視圖不透明化以及做切圓操作吃沪。
7). 不要動態(tài)的add 或者 remove 子控件汤善。最好在初始化時就添加完,然后通過hidden來控制是否顯示票彪。
8). 使用調(diào)試工具分析問題红淡。 -
如何實行cell的動態(tài)的行高
如果希望每條數(shù)據(jù)顯示自身的行高,必須設(shè)置兩個屬性降铸,1.預(yù)估行高在旱,2.自定義行高。
設(shè)置預(yù)估行高 tableView.estimatedRowHeight = 200推掸。
設(shè)置定義行高 tableView.estimatedRowHeight = UITableViewAutomaticDimension桶蝎。
如果要讓自定義行高有效,必須讓容器視圖有一個自下而上的約束终佛。 -
說說你對 block 的理解
棧上的自動復(fù)制到堆上俊嗽,block 的屬性修飾符是 copy,循環(huán)引用的原理和解決方案铃彰。
-
說說你對 runtime 的理解
主要是方法調(diào)用時如何查找緩存绍豁,如何找到方法,找不到方法時怎么轉(zhuǎn)發(fā)牙捉,對象的內(nèi)存布局竹揍。
-
什么是野指針敬飒、空指針?
野指針:不知道指向了哪里的指針叫野指針芬位。即指針指向不確定无拗,指針存的地址是一個垃圾值,未初始化昧碉。
空指針:不指向任何位置的指針叫空指針英染。即指針沒有指向,指針存的地址是一個空地址被饿,NULL四康。 -
什么是 OOA / OOD / OOP ?
OOA(Object Oriented Analysis) --面向?qū)ο蠓治?br> OOD(Object Oriented Design) --面向?qū)ο笤O(shè)計
OOP(Object Oriented Programming)--面向?qū)ο缶幊?/p>10. 多線程是什么
多線程是個復(fù)雜的概念,按字面意思是同步完成多項任務(wù)狭握,提高了資源的使用效率闪金,從硬件、操作系統(tǒng)论颅、應(yīng)用軟件不同的角度去看哎垦,多線程被賦予不同的內(nèi)涵,對于硬件恃疯,現(xiàn)在市面上多數(shù)的CPU都是多核的漏设,多核的CPU運算多線程更為出色;從操作系統(tǒng)角度,是多任務(wù)今妄,現(xiàn)在用的主流操作系統(tǒng)都是多任務(wù)的愿题,可以一邊聽歌、一邊寫博客;對于應(yīng)用來說蛙奖,多線程可以讓應(yīng)用有更快的回應(yīng),可以在網(wǎng)絡(luò)下載時杆兵,同時響應(yīng)用戶的觸摸操作雁仲。在iOS應(yīng)用中,對多線程最初的理解琐脏,就是并發(fā)攒砖,它的含義是原來先做燒水,再摘菜日裙,再炒菜的工作吹艇,會變成燒水的同時去摘菜,最后去炒菜昂拂。
11. iOS 中的多線程
iOS中的多線程受神,是Cocoa框架下的多線程,通過Cocoa的封裝格侯,可以讓我們更為方便的使用線程鼻听,做過C++的同學(xué)可能會對線程有更多的理解财著,比如線程的創(chuàng)立,信號量撑碴、共享變量有認識撑教,Cocoa框架下會方便很多,它對線程做了封裝醉拓,有些封裝伟姐,可以讓我們創(chuàng)建的對象,本身便擁有線程亿卤,也就是線程的對象化抽象愤兵,從而減少我們的工程,提供程序的健壯性怠噪。
GCD是(Grand Central Dispatch)的縮寫 恐似,從系統(tǒng)級別提供的一個易用地多線程類庫,具有運行時的特點傍念,能充分利用多核心硬件矫夷。GCD的API接口為C語言的函數(shù),函數(shù)參數(shù)中多數(shù)有Block憋槐,關(guān)于Block的使用參看這里双藕,為我們提供強大的“接口”,對于GCD的使用參見本文
NSOperation與Queue
NSOperation是一個抽象類阳仔,它封裝了線程的細節(jié)實現(xiàn)忧陪,我們可以通過子類化該對象,加上NSQueue來同面向?qū)ο蟮乃季S近范,管理多線程程序嘶摊。具體可參看這里:一個基于NSOperation的多線程網(wǎng)絡(luò)訪問的項目。
NSThread
NSThread是一個控制線程執(zhí)行的對象评矩,它不如NSOperation抽象叶堆,通過它我們可以方便的得到一個線程,并控制它斥杜。但NSThread的線程之間的并發(fā)控制虱颗,是需要我們自己來控制的,可以通過NSCondition實現(xiàn)蔗喂。
參看 iOS多線程編程之NSThread的使用
其他多線程
在Cocoa的框架下忘渔,通知、Timer和異步函數(shù)等都有使用多線程缰儿,(待補充).
12. 在項目什么時候選擇使用GCD畦粮,什么時候選擇NSOperation?
項目中使用NSOperation的優(yōu)點是NSOperation是對線程的高度抽象,在項目中使用它,會使項目的程序結(jié)構(gòu)更好锈玉,子類化NSOperation的設(shè)計思路爪飘,是具有面向?qū)ο蟮膬?yōu)點(復(fù)用、封裝)拉背,使得實現(xiàn)是多線程支持师崎,而接口簡單,建議在復(fù)雜項目中使用椅棺。
項目中使用GCD的優(yōu)點是GCD本身非常簡單犁罩、易用,對于不復(fù)雜的多線程操作两疚,會節(jié)省代碼量床估,而Block參數(shù)的使用,會是代碼更為易讀诱渤,建議在簡單項目中使用丐巫。
13 KVO,NSNotification勺美,delegate及block區(qū)別
KVO就是cocoa框架實現(xiàn)的觀察者模式递胧,一般同KVC搭配使用,通過KVO可以監(jiān)測一個值的變化赡茸,比如View的高度變化缎脾。是一對多的關(guān)系,一個值的變化會通知所有的觀察者占卧。
NSNotification是通知遗菠,也是一對多的使用場景。在某些情況下华蜒,KVO和NSNotification是一樣的辙纬,都是狀態(tài)變化之后告知對方。NSNotification的特點叭喜,就是需要被觀察者先主動發(fā)出通知牲平,然后觀察者注冊監(jiān)聽后再來進行響應(yīng),比KVO多了發(fā)送通知的一步域滥,但是其優(yōu)點是監(jiān)聽不局限于屬性的變化,還可以對多種多樣的狀態(tài)變化進行監(jiān)聽蜈抓,監(jiān)聽范圍廣启绰,使用也更靈活。
delegate 是代理沟使,就是我不想做的事情交給別人做委可。比如狗需要吃飯,就通過delegate通知主人,主人就會給他做飯着倾、盛飯拾酝、倒水,這些操作卡者,這些狗都不需要關(guān)心蒿囤,只需要調(diào)用delegate(代理人)就可以了,由其他類完成所需要的操作崇决。所以delegate是一對一關(guān)系材诽。
block是delegate的另一種形式,是函數(shù)式編程的一種形式恒傻。使用場景跟delegate一樣脸侥,相比delegate更靈活,而且代理的實現(xiàn)更直觀盈厘。
KVO一般的使用場景是數(shù)據(jù)睁枕,需求是數(shù)據(jù)變化,比如股票價格變化沸手,我們一般使用KVO(觀察者模式)外遇。delegate一般的使用場景是行為,需求是需要別人幫我做一件事情罐氨,比如買賣股票臀规,我們一般使用delegate。
Notification一般是進行全局通知栅隐,比如利好消息一出塔嬉,通知大家去買入。delegate是強關(guān)聯(lián)租悄,就是委托和代理雙方互相知道谨究,你委托別人買股票你就需要知道經(jīng)紀人,經(jīng)紀人也不要知道自己的顧客泣棋。Notification是弱關(guān)聯(lián)胶哲,利好消息發(fā)出,你不需要知道是誰發(fā)的也可以做出相應(yīng)的反應(yīng)潭辈,同理發(fā)消息的人也不需要知道接收的人也可以正常發(fā)出消息鸯屿。
14 將一個函數(shù)在主線程執(zhí)行的4種方法
- GCD方法,通過向主線程隊列發(fā)送一個block塊把敢,使block里的方法可以在主線程中執(zhí)行寄摆。
dispatch_async(dispatch_get_main_queue(), ^{ //需要執(zhí)行的方法});
- NSOperation 方法
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; //主隊列NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ //需要執(zhí)行的方法}];[mainQueue addOperation:operation];
- NSThread 方法
[self performSelector:@selector(method) onThread:[NSThread mainThread] withObject:nil waitUntilDone:YES modes:nil];[self performSelectorOnMainThread:@selector(method) withObject:nil waitUntilDone:YES];[[NSThread mainThread] performSelector:@selector(method) withObject:nil];
- RunLoop方法
[[NSRunLoop mainRunLoop] performSelector:@selector(method) withObject:nil];
15 如何讓計時器調(diào)用一個類方法
- 計時器只能調(diào)用實例方法,但是可以在這個實例方法里面調(diào)用靜態(tài)方法修赞。
- 使用計時器需要注意婶恼,計時器一定要加入RunLoop中,并且選好model才能運行。scheduledTimerWithTimeInterval方法創(chuàng)建一個計時器并加入到RunLoop中所以可以直接使用勾邦。
- 如果計時器的repeats選擇YES說明這個計時器會重復(fù)執(zhí)行蚣录,一定要在合適的時機調(diào)用計時器的invalid。不能在dealloc中調(diào)用眷篇,因為一旦設(shè)置為repeats 為yes萎河,計時器會強持有self,導(dǎo)致dealloc永遠不會被調(diào)用铅歼,這個類就永遠無法被釋放公壤。比如可以在viewDidDisappear中調(diào)用,這樣當類需要被回收的時候就可以正常進入dealloc中了椎椰。
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];-(void)timerMethod{//調(diào)用類方法 [[self class] staticMethod];}-(void)invalid{ [timer invalid]; timer = nil;}
16 如何重寫類方法
- 1厦幅、在子類中實現(xiàn)一個同基類名字一樣的靜態(tài)方法
- 2、在調(diào)用的時候不要使用類名調(diào)用慨飘,而是使用[self class]的方式調(diào)用确憨。原理,用類名調(diào)用是早綁定瓤的,在編譯期綁定休弃,用[self class]是晚綁定,在運行時決定調(diào)用哪個方法圈膏。
17 NSTimer創(chuàng)建后塔猾,會在哪個線程運行。
- 用scheduledTimerWithTimeInterval創(chuàng)建的稽坤,在哪個線程創(chuàng)建就會被加入哪個線程的RunLoop中就運行在哪個線程
- 自己創(chuàng)建的Timer丈甸,加入到哪個線程的RunLoop中就運行在哪個線程。
18 id和NSObject*的區(qū)別
- id是一個 objc_object 結(jié)構(gòu)體指針尿褪,定義是
typedef struct objc_object *id
id可以理解為指向?qū)ο蟮闹羔樐览蕖K衞c的對象 id都可以指向,編譯器不會做類型檢查杖玲,id調(diào)用任何存在的方法都不會在編譯階段報錯顿仇,當然如果這個id指向的對象沒有這個方法,該崩潰還是會崩潰的摆马。
NSObject *指向的必須是NSObject的子類臼闻,調(diào)用的也只能是NSObjec里面的方法否則就要做強制類型轉(zhuǎn)換。
不是所有的OC對象都是NSObject的子類囤采,還有一些繼承自NSProxy述呐。NSObject *可指向的類型是id的子集。
77.ios開發(fā)逆向傳值的幾種方法整理
第一種:代理傳值
第二個控制器:
@protocol WJSecondViewControllerDelegate <NSObject>- (void)changeText:(NSString*)text;@end @property(nonatomic,assign)id<WJSecondViewControllerDelegate>delegate; - (IBAction)buttonClick:(UIButton*)sender {_str = sender.titleLabel.text;[self.delegate changeText:sender.titleLabel.text];[self.navigationController popViewControllerAnimated:YES];}
第一個控制器:
- (IBAction)pushToSecond:(id)sender {WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];svc.delegate = self;svc.str = self.navigationItem.title;[self.navigationController pushViewController:svc animated:YES];[svc release];}- (void)changeText:(NSString *)text{self.navigationItem.title = text;}
第二種:通知傳值
第一個控制器:
//注冊監(jiān)聽通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(limitDataForModel:) name:@"NOV" object:nil];- (void)limitDataForModel:(NSNotification *)noti{self.gamesInfoArray = noti.object;}
第二個控制器:
//發(fā)送通知 [[NSNotificationCenter defaultCenter] postNotificationName:@"NOV" object:gameArray];
第三種:單例傳值
Single是一個單例類斑唬,并且有一個字符串類型的屬性titleName
在第二個控制器:- (IBAction)buttonClick:(UIButton*)sender {Single *single = [Single sharedSingle];single.titleName = sender.titleLabel.text;[self.navigationController popViewControllerAnimated:YES];}
第一個控制器:
- (void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];Single *single = [Single sharedSingle];self.navigationItem.title = single.titleName;}
第四種:block傳值
第二個控制器:
@property (nonatomic,copy) void (^changeText_block)(NSString*);- (IBAction)buttonClick:(UIButton*)sender {_str = sender.titleLabel.text;self.changeText_block(sender.titleLabel.text);[self.navigationController popViewControllerAnimated:YES];}
第一個控制器:
- (IBAction)pushToSecond:(id)sender {WJSecondViewController *svc = [[WJSecondViewController alloc]initWithNibName:@"WJSecondViewController" bundle:nil];svc.str = self.navigationItem.title;[svc setChangeText_block:^(NSString *str) { >self.navigationItem.title = str;}];[self.navigationController pushViewController:svc animated:YES];}
第五種:extern傳值
第二個控制器:
extern NSString *btn;- (IBAction)buttonClick:(UIButton*)sender {btn = sender.titleLabel.text;[self.navigationController popViewControllerAnimated:YES];}
第一個控制器:
NSString *btn = nil;- (void)viewWillAppear:(BOOL)animated{[super viewWillAppear:animated];self.navigationItem.title = btn;}
第六種:KVO傳值
第一個控制器:
- (void)viewDidLoad {[super viewDidLoad]; _vc =[[SecondViewController alloc]init];//self監(jiān)聽vc里的textValue屬性[_vc addObserver:self forKeyPath:@"textValue" options:0 context:nil]; }
第二個控制器:
- (IBAction)buttonClicked:(id)sender {self.textValue = self.textField.text;[self.navigationController popViewControllerAnimated:YES];}
78.淺談iOS開發(fā)中方法延遲執(zhí)行的幾種方式
Method1. performSelector方法
Method2. NSTimer定時器
Method3. NSThread線程的sleep
Method4. GCD
公用延遲執(zhí)行方法
- (void)delayMethod{ NSLog(@"delayMethodEnd");}
Method1:performSelector
[self performSelector:@selector(delayMethod) withObject:nil/*可傳任意類型參數(shù)*/ afterDelay:2.0];
注:此方法是一種非阻塞的執(zhí)行方式,未找到取消執(zhí)行的方法恕刘。程序運行結(jié)束
2015-08-31 10:56:59.361 CJDelayMethod[1080:39604] delayMethodStart2015-08-31 10:56:59.363 CJDelayMethod[1080:39604] nextMethod2015-08-31 10:57:01.364 CJDelayMethod[1080:39604] delayMethodEnd
Method2:NSTimer定時器
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
注:此方法是一種非阻塞的執(zhí)行方式缤谎,
取消執(zhí)行方法:- (void)invalidate;
即可程序運行結(jié)束
2015-08-31 10:58:10.182 CJDelayMethod[1129:41106] delayMethodStart2015-08-31 10:58:10.183 CJDelayMethod[1129:41106] nextMethod2015-08-31 10:58:12.185 CJDelayMethod[1129:41106] delayMethodEnd
Method3:NSThread線程的sleep
[NSThread sleepForTimeInterval:2.0];
注:此方法是一種阻塞執(zhí)行方式,建議放在子線程中執(zhí)行褐着,否則會卡住界面坷澡。但有時還是需要阻塞執(zhí)行,如進入歡迎界面需要沉睡3秒才進入主界面時含蓉。
沒有找到取消執(zhí)行方式频敛。程序運行結(jié)束
2015-08-31 10:58:41.501 CJDelayMethod[1153:41698] delayMethodStart2015-08-31 10:58:43.507 CJDelayMethod[1153:41698] nextMethod
Method4:GCD
__block ViewController/*主控制器*/ *weakSelf = self;
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0/*延遲執(zhí)行時間*/ * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{ [weakSelf delayMethod];});`
注:此方法可以在參數(shù)中選擇執(zhí)行的線程,是一種非阻塞執(zhí)行方式馅扣。沒有找到取消執(zhí)行方式斟赚。
程序運行結(jié)束
2015-08-31 10:59:21.652 CJDelayMethod[1181:42438] delayMethodStart2015-08-31 10:59:21.653 CJDelayMethod[1181:42438] nextMethod2015-08-31 10:59:23.653 CJDelayMethod[1181:42438] delayMethodEnd
完整代碼參見:
//
// ViewController.m
// CJDelayMethod
//
// Created by 陳杰 on 8/31/15.
// Copyright (c) 2015 chenjie. All rights reserved.
//import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSTimer timer;
@end
@implementation ViewController- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"delayMethodStart");
[self methodOnePerformSelector];//
[self methodTwoNSTimer];//
[self methodThreeSleep];//
[self methodFourGCD];
NSLog(@"nextMethod");
}
- (void)methodFiveAnimation{
[UIView animateWithDuration:0 delay:2.0 options:UIViewAnimationOptionAllowUserInteraction animations:^{ } completion:^(BOOL finished) {
[self delayMethod];
}];
}
- (void)methodFourGCD{
__block ViewController
weakSelf = self; dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); dispatch_after(delayTime, dispatch_get_main_queue(), ^{
[weakSelf delayMethod];
});
}
- (void)methodThreeSleep{
[NSThread sleepForTimeInterval:2.0];
}
- (void)methodTwoNSTimer{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(delayMethod) userInfo:nil repeats:NO];
}
- (void)methodOnePerformSelector{
[self performSelector:@selector(delayMethod) withObject:nil/*可傳任意類型參數(shù)*/ afterDelay:2.0];
}
- (void)delayMethod{
NSLog(@"delayMethodEnd");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
79.NSPersistentStoreCoordinator , NSManaged0bjectContext 和NSManaged0bject中的那些需要在線程中創(chuàng)建或者傳遞
答:NSPersistentStoreCoordinator是持久化存儲協(xié)調(diào)者,主要用于協(xié)調(diào)托管對象上下文和持久化存儲區(qū)之間的關(guān)系差油。NSManagedObjectContext使用協(xié)調(diào)者的托管對象模型將數(shù)據(jù)保存到數(shù)據(jù)庫拗军,或查詢數(shù)據(jù)。
80.您是否做過一部的網(wǎng)絡(luò)處理和通訊方面的工作蓄喇?如果有发侵,能具體介紹一下實現(xiàn)策略么
答:使用NSOperation發(fā)送異步網(wǎng)絡(luò)請求,使用NSOperationQueue管理線程數(shù)目及優(yōu)先級妆偏,底層是用NSURLConnetion刃鳄,
81.你使用過Objective-C的運行時編程(Runtime Programming)么?如果使用過钱骂,你用它做了什么叔锐?你還能記得你所使用的相關(guān)的頭文件或者某些方法的名稱嗎?
答:Objecitve-C的重要特性是Runtime(運行時),在#import <objc/runtime.h> 下能看到相關(guān)的方法罐柳,用過objc_getClass()和class_copyMethodList()獲取過私有API;使用
Method method1 = class_getInstanceMethod(cls, sel1); Method method2 = class_getInstanceMethod(cls, sel2); method_exchangeImplementations(method1, method2);
代碼交換兩個方法掌腰,在寫unit test時使用到。
82.Core開頭的系列的內(nèi)容张吉。是否使用過CoreAnimation和CoreGraphics齿梁。UI框架和CA,CG框架的聯(lián)系是什么肮蛹?分別用CA和CG做過些什么動畫或者圖像上的內(nèi)容制肮。(有需要的話還可以涉及Quartz的一些內(nèi)容)
答:UI框架的底層有CoreAnimation吞滞,CoreAnimation的底層有CoreGraphics。
UIKit Core Animation Core Graphics Graphics Hardware 使用CA做過menu菜單的展開收起(太遜了)
83.是否使用過CoreText或者CoreImage等?如果使用過考婴,請談?wù)勀闶褂肅oreText或者CoreImage的體驗。
答:CoreText可以解決復(fù)雜文字內(nèi)容排版問題憨琳。CoreImage可以處理圖片,為其添加各種效果邻储。體驗是很強大,挺復(fù)雜的旧噪。
85.NSNotification和KVO的區(qū)別和用法是什么吨娜?什么時候應(yīng)該使用通知,什么時候應(yīng)該使用KVO淘钟,它們的實現(xiàn)上有什么區(qū)別嗎宦赠?如果用protocol和delegate(或者delegate的Array)來實現(xiàn)類似的功能可能嗎?如果可能米母,會有什么潛在的問題勾扭?如果不能,為什么铁瞒?(雖然protocol和delegate這種東西面試已經(jīng)面爛了…)
答:NSNotification是通知模式在iOS的實現(xiàn)妙色,KVO的全稱是鍵值觀察(Key-value observing),其是基于KVC(key-value coding)的,KVC是一個通過屬性名訪問屬性變量的機制精拟。例如將Module層的變化燎斩,通知到多個Controller對象時,可以使用NSNotification蜂绎;如果是只需要觀察某個對象的某個屬性栅表,可以使用KVO。
對于委托模式师枣,在設(shè)計模式中是對象適配器模式怪瓶,其是delegate是指向某個對象的,這是一對一的關(guān)系践美,而在通知模式中洗贰,往往是一對多的關(guān)系。委托模式陨倡,從技術(shù)上可以現(xiàn)在改變delegate指向的對象敛滋,但不建議這樣做,會讓人迷惑兴革,如果一個delegate對象不斷改變绎晃,指向不同的對象。86.你用過NSOperationQueue么杂曲?如果用過或者了解的話庶艾,你為什么要使用NSOperationQueue,實現(xiàn)了什么擎勘?請描述它和G.C.D的區(qū)別和類似的地方(提示:可以從兩者的實現(xiàn)機制和適用范圍來描述)咱揍。
答:使用NSOperationQueue用來管理子類化的NSOperation對象,控制其線程并發(fā)數(shù)目棚饵。GCD和NSOperation都可以實現(xiàn)對線程的管理煤裙,區(qū)別是 NSOperation和NSOperationQueue是多線程的面向?qū)ο蟪橄笱谕辍m椖恐惺褂肗SOperation的優(yōu)點是NSOperation是對線程的高度抽象,在項目中使用它硼砰,會使項目的程序結(jié)構(gòu)更好藤为,子類化NSOperation的設(shè)計思路,是具有面向?qū)ο蟮膬?yōu)點(復(fù)用夺刑、封裝),使得實現(xiàn)是多線程支持分别,而接口簡單遍愿,建議在復(fù)雜項目中使用。
項目中使用GCD的優(yōu)點是GCD本身非常簡單耘斩、易用沼填,對于不復(fù)雜的多線程操作,會節(jié)省代碼量括授,而Block參數(shù)的使用坞笙,會是代碼更為易讀,建議在簡單項目中使用荚虚。87.既然提到G.C.D薛夜,那么問一下在使用G.C.D以及block時要注意些什么?它們兩是一回事兒么版述?block在ARC中和傳統(tǒng)的MRC中的行為和用法有沒有什么區(qū)別梯澜,需要注意些什么?
答:使用block是要注意渴析,若將block做函數(shù)參數(shù)時晚伙,需要把它放到最后,GCD是Grand Central Dispatch俭茧,是一個對線程開源類庫咆疗,而Block是閉包,是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)母债。
88. 對于Objective-C午磁,你認為它最大的優(yōu)點和最大的不足是什么?對于不足之處场斑,現(xiàn)在有沒有可用的方法繞過這些不足來實現(xiàn)需求漓踢。如果可以的話,你有沒有考慮或者實踐過重新實現(xiàn)OC的一些功能漏隐,如果有喧半,具體會如何做?
答:最大的優(yōu)點是它的運行時特性青责,不足是沒有命名空間挺据,對于命名沖突取具,可以使用長命名法或特殊前綴解決,如果是引入的第三方庫之間的命名沖突扁耐,可以使用link命令及flag解決沖突暇检。
89. 你實現(xiàn)過一個框架或者庫以供別人使用么?如果有婉称,請談一談構(gòu)建框架或者庫時候的經(jīng)驗块仆;如果沒有,請設(shè)想和設(shè)計框架的public的API王暗,并指出大概需要如何做悔据、需要注意一些什么方面,來使別人容易地使用你的框架俗壹。
答:抽象和封裝科汗,方便使用。首先是對問題有充分的了解绷雏,比如構(gòu)建一個文件解壓壓縮框架头滔,從使用者的角度出發(fā),只需關(guān)注發(fā)送給框架一個解壓請求涎显,框架完成復(fù)雜文件的解壓操作坤检,并且在適當?shù)臅r候通知給是哦難過者,如解壓完成期吓、解壓出錯等缀蹄。在框架內(nèi)部去構(gòu)建對象的關(guān)系,通過抽象讓其更為健壯膘婶、便于更改缺前。其次是API的說明文檔。
90.說說你理解的埋點悬襟?
以下幾篇文章寫的相當不錯衅码,可以適當借鑒下!
iOS無埋點數(shù)據(jù)SDK的整體設(shè)計與技術(shù)實現(xiàn)
iOS無埋點SDK 之 RN頁面的數(shù)據(jù)收集
****91.消息轉(zhuǎn)發(fā)機制原理脊岳?
消息轉(zhuǎn)發(fā)機制基本分為三個步驟:
1逝段、動態(tài)方法解析
2、備用接受者
3割捅、完整轉(zhuǎn)發(fā)
轉(zhuǎn)發(fā)機制原理
新建一個HelloClass的類奶躯,定義兩個方法:
@interfaceHelloClass:NSObject
- (``void``)hello;
+ (HelloClass *)hi;@end
|
動態(tài)方法解析
對象在接收到未知的消息時,首先會調(diào)用所屬類的類方法+resolveInstanceMethod:(實例方法)或者+resolveClassMethod:(類方法)亿驾。在這個方法中嘹黔,我們有機會為該未知消息新增一個”處理方法”“。不過使用該方法的前提是我們已經(jīng)實現(xiàn)了該”處理方法”莫瞬,只需要在運行時通過class_addMethod函數(shù)動態(tài)添加到類里面就可以了儡蔓。
void
functionForMethod(id self, SEL _cmd)
{
NSLog(@``"Hello!"``);
}
Class functionForClassMethod(id self, SEL _cmd)
{
NSLog(@``"Hi!"``);
return
[HelloClass ``class``];
}
#pragma mark - 1郭蕉、動態(tài)方法解析
+ (BOOL)resolveClassMethod:(SEL)sel
{
NSLog(@``"resolveClassMethod"``);
NSString *selString = NSStringFromSelector(sel);
if
([selString isEqualToString:@``"hi"``])
{
Class metaClass = objc_getMetaClass(``"HelloClass"``);
class_addMethod(metaClass, @selector(hi), (IMP)functionForClassMethod, ``"v@:"``);
return
YES;
}
return
[``super
resolveClassMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@``"resolveInstanceMethod"``);
NSString *selString = NSStringFromSelector(sel);
if
([selString isEqualToString:@``"hello"``])
{
class_addMethod(self, @selector(hello), (IMP)functionForMethod, ``"v@:"``);
return
YES;
}
return
[``super
resolveInstanceMethod:sel];
}
備用接受者
動態(tài)方法解析無法處理消息,則會走備用接受者喂江。這個備用接受者只能是一個新的對象召锈,不能是self本身,否則就會出現(xiàn)無限循環(huán)获询。如果我們沒有指定相應(yīng)的對象來處理aSelector涨岁,則應(yīng)該調(diào)用父類的實現(xiàn)來返回結(jié)果。
#pragma mark - 2吉嚣、備用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@``"forwardingTargetForSelector"``);
NSString *selectorString = NSStringFromSelector(aSelector);
// 將消息交給_helper來處理? ? if ([selectorString isEqualToString:@"hello"]) {
return
_helper;
}
return
[``super
forwardingTargetForSelector:aSelector];
}
|
在本類中需要實現(xiàn)這個新的接受對象
@interfaceHelloClass()
{
RuntimeMethodHelper *_helper;
}
@end
@implementationHelloClass- (instancetype)init
{
self = [``super
init];
if
(self)
{
_helper = [RuntimeMethodHelper ``new``];
}
return
self;
}
|
#``import``"RuntimeMethodHelper.h"
@implementationRuntimeMethodHelper- (``void``)hello
{
NSLog(@``"%@, %p"``, self, _cmd);
}@end
完整消息轉(zhuǎn)發(fā)
如果動態(tài)方法解析和備用接受者都沒有處理這個消息卵惦,那么就會走完整消息轉(zhuǎn)發(fā):
#pragma mark - 3、完整消息轉(zhuǎn)發(fā)
- (``void``)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@``"forwardInvocation"``);
if
([RuntimeMethodHelper instancesRespondToSelector:anInvocation.selector]) {
[anInvocation invokeWithTarget:_helper];
}
}
|
/必須重新這個方法瓦戚,消息轉(zhuǎn)發(fā)機制使用從這個方法中獲取的信息來創(chuàng)建NSInvocation對象/**
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSMethodSignature *signature = [``super
methodSignatureForSelector:aSelector];
if
(!signature)
{
if
([RuntimeMethodHelper instancesRespondToSelector:aSelector])
{
signature = [RuntimeMethodHelper instanceMethodSignatureForSelector:aSelector];
}
}
return
signature;
}
|
****92.說說你理解weak屬性?
weak實現(xiàn)原理:
Runtime維護了一個weak表丛塌,用于存儲指向某個對象的所有weak指針较解。weak表其實是一個hash(哈希)表,Key是所指對象的地址赴邻,Value是weak指針的地址(這個地址的值是所指對象的地址)數(shù)組印衔。
1、初始化時:runtime會調(diào)用objc_initWeak函數(shù)姥敛,初始化一個新的weak指針指向?qū)ο蟮牡刂贰?/p>
2奸焙、添加引用時:objc_initWeak函數(shù)會調(diào)用 objc_storeWeak() 函數(shù), objc_storeWeak() 的作用是更新指針指向彤敛,創(chuàng)建對應(yīng)的弱引用表与帆。
3、釋放時墨榄,調(diào)用clearDeallocating函數(shù)玄糟。clearDeallocating函數(shù)首先根據(jù)對象地址獲取所有weak指針地址的數(shù)組,然后遍歷這個數(shù)組把其中的數(shù)據(jù)設(shè)為nil袄秩,最后把這個entry從weak表中刪除阵翎,最后清理對象的記錄。
追問的問題一:
1.實現(xiàn)weak后之剧,為什么對象釋放后會自動為nil郭卫?
runtime?對注冊的類, 會進行布局背稼,對于?weak?對象會放入一個?hash?表中贰军。 用?weak?指向的對象內(nèi)存地址作為?key,當此對象的引用計數(shù)為?0?的時候會?dealloc蟹肘,假如?weak?指向的對象內(nèi)存地址是?a?谓形,那么就會以?a?為鍵灶伊, 在這個?weak?表中搜索,找到所有以?a?為鍵的?weak?對象寒跳,從而設(shè)置為?nil?聘萨。
追問的問題二:
2.當weak引用指向的對象被釋放時,又是如何去處理weak指針的呢童太?
1米辐、調(diào)用objc_release
2、因為對象的引用計數(shù)為0书释,所以執(zhí)行dealloc
3翘贮、在dealloc中,調(diào)用了_objc_rootDealloc函數(shù)
4爆惧、在_objc_rootDealloc中狸页,調(diào)用了object_dispose函數(shù)
5、調(diào)用objc_destructInstance
6扯再、最后調(diào)用objc_clear_deallocating,詳細過程如下:
a. 從weak表中獲取廢棄對象的地址為鍵值的記錄
b. 將包含在記錄中的所有附有 weak修飾符變量的地址芍耘,賦值為 nil
c. 將weak表中該記錄刪除
d. 從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄
****90.假如Controller太臃腫,如何優(yōu)化熄阻?
1.將網(wǎng)絡(luò)請求抽象到單獨的類中
方便在基類中處理公共邏輯斋竞;
方便在基類中處理緩存邏輯,以及其它一些公共邏輯秃殉;
方便做對象的持久化坝初。
2.將界面的封裝抽象到專門的類中
構(gòu)造專門的 UIView 的子類,來負責(zé)這些控件的拼裝钾军。這是最徹底和優(yōu)雅的方式鳄袍,不過稍微麻煩一些的是,你需要把這些控件的事件回調(diào)先接管吏恭,再都一一暴露回 Controller畦木。
3.構(gòu)造 ViewModel
借鑒MVVM。具體做法就是將 ViewController 給 View 傳遞數(shù)據(jù)這個過程砸泛,抽象成構(gòu)造 ViewModel 的過程十籍。
4.專門構(gòu)造存儲類
專門來處理本地數(shù)據(jù)的存取。
5.整合常量
****90.項目中網(wǎng)絡(luò)層如何做安全處理唇礁?
1勾栗、盡量使用https
https可以過濾掉大部分的安全問題。https在證書申請盏筐,服務(wù)器配置围俘,性能優(yōu)化,客戶端配置上都需要投入精力,所以缺乏安全意識的開發(fā)人員容易跳過https界牡,或者拖到以后遇到問題再優(yōu)化簿寂。https除了性能優(yōu)化麻煩一些以外其他都比想象中的簡單,如果沒精力優(yōu)化性能宿亡,至少在注冊登錄模塊需要啟用https常遂,這部分業(yè)務(wù)對性能要求比較低。
2挽荠、不要傳輸明文密碼
不知道現(xiàn)在還有多少app后臺是明文存儲密碼的克胳。無論客戶端,server還是網(wǎng)絡(luò)傳輸都要避免明文密碼圈匆,要使用hash值漠另。客戶端不要做任何密碼相關(guān)的存儲跃赚,hash值也不行笆搓。存儲token進行下一次的認證,而且token需要設(shè)置有效期纬傲,使用refresh
token去申請新的token满败。
3、Post并不比Get安全
事實上嘹锁,Post和Get一樣不安全,都是明文着裹。參數(shù)放在QueryString或者Body沒任何安全上的差別领猾。在Http的環(huán)境下,使用Post或者Get都需要做加密和簽名處理骇扇。
4摔竿、不要使用301跳轉(zhuǎn)
301跳轉(zhuǎn)很容易被Http劫持攻擊。移動端http使用301比桌面端更危險少孝,用戶看不到瀏覽器地址继低,無法察覺到被重定向到了其他地址。如果一定要使用稍走,確保跳轉(zhuǎn)發(fā)生在https的環(huán)境下袁翁,而且https做了證書綁定校驗。
5婿脸、http請求都帶上MAC
所有客戶端發(fā)出的請求粱胜,無論是查詢還是寫操作,都帶上MAC(Message Authentication
Code)狐树。MAC不但能保證請求沒有被篡改(Integrity)焙压,還能保證請求確實來自你的合法客戶端(Signing)。當然前提是你客戶端的key沒有被泄漏,如何保證客戶端key的安全是另一個話題涯曲。MAC值的計算可以簡單的處理為hash(request
params+key)野哭。帶上MAC之后,服務(wù)器就可以過濾掉絕大部分的非法請求幻件。MAC雖然帶有簽名的功能拨黔,和RSA證書的電子簽名方式卻不一樣,原因是MAC簽名和簽名驗證使用的是同一個key傲武,而RSA是使用私鑰簽名蓉驹,公鑰驗證,MAC的簽名并不具備法律效應(yīng)揪利。
****6态兴、http請求使用臨時密鑰
高延遲的網(wǎng)絡(luò)環(huán)境下,不經(jīng)優(yōu)化https的體驗確實會明顯不如http疟位。在不具備https條件或?qū)W(wǎng)絡(luò)性能要求較高且缺乏https優(yōu)化經(jīng)驗的場景下瞻润,http的流量也應(yīng)該使用AES進行加密。AES的密鑰可以由客戶端來臨時生成甜刻,不過這個臨時的AES
key需要使用服務(wù)器的公鑰進行加密绍撞,確保只有自己的服務(wù)器才能解開這個請求的信息,當然服務(wù)器的response也需要使用同樣的AES
key進行加密得院。由于http的應(yīng)用場景都是由客戶端發(fā)起傻铣,服務(wù)器響應(yīng),所以這種由客戶端單方生成密鑰的方式可以一定程度上便捷的保證通信安全祥绞。
****7非洲、AES使用CBC模式
不要使用ECB模式,記得設(shè)置初始化向量蜕径,每個block加密之前要和上個block的秘文進行運算两踏。
****95.main()之前的過程有哪些?
1兜喻、main之前的加載過程
1)dyld 開始將程序二進制文件初始化
2)交由ImageLoader 讀取 image梦染,其中包含了我們的類,方法等各種符號(Class朴皆、Protocol 帕识、Selector、 IMP)
3)由于runtime 向dyld 綁定了回調(diào)遂铡,當image加載到內(nèi)存后渡冻,dyld會通知runtime進行處理
4)runtime 接手后調(diào)用map_images做解析和處理
5)接下來load_images 中調(diào)用call_load_methods方法,遍歷所有加載進來的Class忧便,按繼承層次依次調(diào)用Class的+load和其他Category的+load方法
6)至此 所有的信息都被加載到內(nèi)存中
7)最后dyld調(diào)用真正的main函數(shù)