1. iOS單例的實現(xiàn)方式亭病?
之前總是這樣寫:
static Singleton *shareSingleton = nil;
? (instancetype)shareSingleton {
? ? static dispatch_once_t onceToken;
? ? dispatch_once(&onceToken, ^{
? ? ? ? shareSingleton = [[self alloc] init];
? ? });
? ? return shareSingleton;
}
可能創(chuàng)建多次的原因:當(dāng)使用alloc init創(chuàng)建對象時供屉,在調(diào)用alloc方法時,oc內(nèi)部會調(diào)用allocWithZone這個方法來申請內(nèi)存竞惋,為
了避免allocWithZone申請新的內(nèi)存,可以重寫allocWithZone方法源祈,在該方法中調(diào)用shareSingleton方法返回單例對象榆苞。拷貝對
象也是同樣的道理羹铅。
可以這樣寫:
static Singleton *shareSingleton = nil;
? (instancetype)shareSingleton {
? ? static dispatch_once_t onceToken;
? ? dispatch_once(&onceToken, ^{
? ? ? ? shareSingleton = [[super allocWithZone:NULL] init];
? ? });
? ? return shareSingleton;
}
? (instancetype)allocWithZone:(struct _NSZone *)zone {
? ? return [Singleton shareSingleton];
}
- (id)copyWithZone:(struct _NSZone *)zone {
? ? return [Singleton shareSingleton];
}
2.?哪些類不適合使用單例模式蚀狰?即使他們在周期中只會出現(xiàn)一次?
工具類,不需要存儲數(shù)據(jù)的 例如判斷郵箱 電話 字符串是否為純數(shù)字等 可以寫成方法
3.?timer是否精準(zhǔn), 怎么使用精準(zhǔn)的定時器?
正常情況下是精準(zhǔn)的职员,但遇到一下情況就會不準(zhǔn)確:
RunLoop的影響:
定時器被添加在主線程中麻蹋,由于定時器在一個RunLoop中被檢測一次,所以如果在這一次的RunLoop中做了耗時的操作廉邑,當(dāng)前
RunLoop持續(xù)的時間超過了定時器的間隔時間哥蔚,那么下一次定時就被延后。
解決辦法:
1蛛蒙、在子線程中創(chuàng)建timer糙箍,在主線程進行定時任務(wù)的操作
2、在子線程中創(chuàng)建timer牵祟,在子線程中進行定時任務(wù)的操作深夯,需要UI操作時切換回主線程進行操作
RunLoop模式的影響:
為了驗證,我們在當(dāng)前頁面上添加一個tableview,在定時器運行時咕晋,我們對tableview進行滑動操作雹拄,可以發(fā)現(xiàn),定時器并不
會觸發(fā)下一次的定時任務(wù)掌呜。
原因分析:
主線程有兩種預(yù)設(shè)模式滓玖,分別是RunLoopDefaultModel 和TrackingRunLoopModel 當(dāng)定時器添加到主線程且沒有執(zhí)行的運行模
式時,一般會 默認(rèn)添加到RunLoopDefaultModel 一般情況下定時器會正常觸發(fā)定時任務(wù) 但當(dāng)用戶進行滑動操作時质蕉,主線程就
會切換成TrackingRunLoopModel 在此模式下定時器不會被觸發(fā)
解決方法:
添加定時器到主線程的CommonMode中或者子線程中
[[NSRunLoop mainRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
其他方式的Timer:
使用mach_absolute_time()來實現(xiàn)更高精度的定時器:
iPhone上有這么一個均勻變化的東西來提供給我們作為時間參考势篡,就是CPU的時鐘周期數(shù)(ticks)。
通過mach_absolute_time()獲取CPU已運行的tick數(shù)量模暗。將tick數(shù)經(jīng)過轉(zhuǎn)換變成秒或者納秒禁悠,從而實現(xiàn)時間的計算。
GCD定時器:
RunLoop是dispatch_source_t實現(xiàn)的timer兑宇,所以理論上來說碍侦,GCD定時器的精度比NSTimer只高不低
NSTimeInterval interval = 1.0;
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
dispatch_source_set_timer(_timer, dispatch_walltime(NULL,0), interval *NSEC_PER_SEC,0);
dispatch_source_set_event_handler(_timer, ^{
? ? NSLog(@"GCD timer test");
});
dispatch_resume(_timer);
4.?-id、instancetype隶糕、NSObject *和id *的區(qū)別?
首先需要知道瓷产,在cocoa的開發(fā)環(huán)境里,NSObject是所有類的根類
id:
從定義來看若厚,id就是一個isa指針拦英,可以指向任何一個繼承了Object(或者NSObject)類的對象
id可以簡單理解為一個萬能指針
id是動態(tài)數(shù)據(jù)類型,編譯時編譯器不會檢查id對象的類型测秸,只有在運行時動態(tài)檢查后會報錯疤估。
id 可用于定義變量 定義方法返回值? ?定義方法參數(shù)。
instancetype:
instancetype意思為實例化霎冯,instancetype與和id一樣铃拇,都可以指向一個繼承了Object(或者NSObject)類的對象
區(qū)別在于:instancetype只能作為方法返回值,需要返回該方法所在的類的實例化對象沈撞,所以instancetype也被稱為關(guān)聯(lián)返回類型慷荔。
使用instancetype會在編譯時進行類型檢查,有利于開發(fā)者在編譯階段發(fā)現(xiàn)錯誤缠俺。
關(guān)聯(lián)返回類型的方法:
以 alloc 或 new 開頭的類方法
以 autorelease显晶、init、retain 或 self 開頭的實例方法壹士。
NSObject*:
NSObject* 就是指向NSObject類型的指針了磷雇,可以指向任何一個繼承了NSObject的對象,感覺和上面的id躏救、instancetype也沒有區(qū)別唯笙。
區(qū)別在于NSObject* 是靜態(tài)數(shù)據(jù)類型螟蒸,編譯時會進行類型檢查。
NSObject*的作用與id一致崩掘。
id<NSObject> *
id<NSObject> *就是指該對象的類型可以是任何一個NSObject或繼承了NSObject的子類七嫌,但該對象必須要遵循<NSObject>協(xié)議(奇葩的命名:協(xié)議名與類名一樣)。簡單來說就是它不關(guān)心對象是什么類型苞慢,只要遵循<NSObject>協(xié)議即可诵原。
在定義delegate時常用。
5. iOS的簽名機制枉疼?
因為蘋果的安全策略皮假,通過簽名機制保證手機上的每個App都是經(jīng)過蘋果認(rèn)證的。
通過App Store安裝
開發(fā)者可以通過Xcode安裝
Ad-Hoc?測試證書打包的App骂维,數(shù)量限制100
In-House?企業(yè)版證書打包App,信任企業(yè)證書后可以使用
由蘋果生成一對公私鑰贺纲,公鑰內(nèi)置與iOS設(shè)備中航闺,私鑰由蘋果保管。
開發(fā)者上傳App給蘋果審核后猴誊,蘋果用私鑰對App數(shù)據(jù)進行簽名潦刃,發(fā)布至App Store。
iOS設(shè)備下載App后懈叹,用公鑰進行驗證乖杠,若正確,則證明App是由蘋果認(rèn)證過的澄成。
6.?+load和+initilaze在分類胧洒,父類,子類和main函數(shù)的調(diào)用順序墨状?
+load加載順序:父類卫漫,子類,分類肾砂。如果多個分類會按照PBXSourcesBuildPhase中順序逐個調(diào)用列赎。
+initialize加載順序:首先有分類時,最后被load的分類會覆蓋類的該方法镐确。然后先父類包吝,再子類,直到第一次被調(diào)用的類源葫。
7.? drawRect方法诗越?
- (void)drawRect:(CGRect)rect;中去繪制一些我們所需要的圖形,如虛線臼氨、圓形掺喻、方形以及曲線等等圖形。
1.? 我們只能在繼承了UIView的子類中通過重寫drawRect方法來繪制圖形。
2.? 如果需要繪制圖形的子類直接繼承自UIView感耙,則子類的drawRect方法中不需要調(diào)用父類方法[super drawRect:rect]褂乍;。如果子
類繼承自其他繼承UIView的View類即硼,則drawRect方法中需要調(diào)用父類方法[super drawRect:rect]逃片;
3.? drawRect方法不能手動直接調(diào)用,我們可以通過調(diào)用其他方法來實現(xiàn)drawRect方法的調(diào)用只酥。如:在子類初始化時調(diào)用- (instancetype)initWithFrame:(CGRect)frame方法褥实,且frame不為CGRectZero時。
4.??我們可以調(diào)用setNeedsDisplay()方法或setNeedsDisplayInRect方法裂允,但是該方法不會自己調(diào)用drawRect方法损离,而是會標(biāo)記視圖,并在下一次循環(huán)更新的時候讓視圖通過drawRect來進行重繪绝编,前提是rect不為CGRectZero僻澎。
8.?main()之前的過程有哪些?
Pre-mian 大概過程主要分為:Load dylibs十饥、Rebase窟勃、Bind、Objc逗堵、Initializers 這幾個步驟
1. dyld 開始將程序二進制文件初始化
2.?dyld 首先會讀取鏡像文件秉氧,然后遞歸的查找動態(tài)庫,利用 ImageLoader 來將其加載到內(nèi)存中
3.?交由ImageLoader 讀取 image蜒秤,其中包含了我們的類汁咏,方法等各種符號
4.?接下來再調(diào)用 load_image,遍歷調(diào)用類的 load 方法垦藏、調(diào)用C++的構(gòu)造函數(shù)屬性函數(shù)梆暖、創(chuàng)建非基本類型的C++靜態(tài)全局變量等等。
9. 簡述消息轉(zhuǎn)發(fā)機制掂骏?
從全局來看轰驳,消息轉(zhuǎn)發(fā)機制共分為3大步驟:
第一步:Method resolution 方法解析處理階段:
對象在收到無法解讀的消息后,首先會調(diào)用+(BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:
(SEL)sel, 詢問是否有動態(tài)添加方法來進行處理弟灼,
+(BOOL)resolveInstanceMethod:(SEL)sel(
if(sel==@selector(speak)){
class_addMethod([selfclass],sel,(IMP)speak,"V@:");
returnYES;
}return[
superresolveInstanceMethod:sel];
}
如果在上一步的2個方法內(nèi)返回的為YES則能接受消息 NO不能接受消息?
第二步:?在-forwardingTargetForSelector:進行判是否轉(zhuǎn)發(fā)給有現(xiàn)實這個方法的對象或者類.
-(id)forwardingTargetForSelector:(SEL)aSelector{
Helper*helper=[[Helper alloc]init];
if([helper respondsToSelector:aSelector]){
returnhelper;}
else{returnnil;
}
}
第三步:?實現(xiàn) -methodSignatureForSelector: 通過正確的類型方法方法簽名
-(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{
return[NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
事不過三,何況操過三次了.處理基本都已經(jīng)結(jié)束了.
實現(xiàn):-doesNotRecognizeSelector: 在此方法中拋出錯誤! 然后奔潰
10.?objc中向一個nil對象發(fā)送消息將會發(fā)生什么级解?
objc在向一個對象發(fā)送消息時,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類田绑,然后在該類中的方法列表以及其父類
方法列表中尋找方法運行勤哗,然后在發(fā)送消息的時候,objc_msgSend方法不會返回值掩驱,所謂的返回內(nèi)容都是具體調(diào)用時執(zhí)行的芒划。?
那么冬竟,回到本題,如果向一個nil對象發(fā)送消息民逼,首先在尋找對象的isa指針時就是0地址返回了泵殴,所以不會出現(xiàn)任何錯誤。
11.?oc的反射機制?
Class對象其實本質(zhì)上就是一個結(jié)構(gòu)體拼苍,這個結(jié)構(gòu)體中的成員變量還是自己笑诅,這種設(shè)計方式非常像鏈表的數(shù)據(jù)結(jié)構(gòu)。
可以直接用一個實例對象或類對象疮鲫,直接調(diào)用Class方法吆你,都可以獲取Class對象。
// SEL和字符串轉(zhuǎn)換
FOUNDATION_EXPORT NSString*NSStringFromSelector(SEL aSelector);
FOUNDATION_EXPORT SELNSSelectorFromString(NSString*aSelectorName);
// Class和字符串轉(zhuǎn)換
FOUNDATION_EXPORT NSString*NSStringFromClass(Class aClass);
FOUNDATION_EXPORT Class __nullableNSClassFromString(NSString*aClassName);
// Protocol和字符串轉(zhuǎn)換FOUNDATION_EXPORT NSString*NSStringFromProtocol(Protocol*proto)NS_AVAILABLE(10_5,2_0);
FOUNDATION_EXPORT Protocol*__nullableNSProtocolFromString(NSString*namestr)NS_AVAILABLE(10_5,2_0);
// 當(dāng)前對象是否這個類或其子類的實例
-(BOOL)isKindOfClass:(Class)aClass;
// 當(dāng)前對象是否是這個類的實例
-(BOOL)isMemberOfClass:(Class)aClass;
// 當(dāng)前對象是否遵守這個協(xié)議
-(BOOL)conformsToProtocol:(Protocol*)aProtocol;
// 當(dāng)前對象是否實現(xiàn)這個方法
-(BOOL)respondsToSelector:(SEL)aSelector;
可以解決的實際問題:
假設(shè)有一天公司產(chǎn)品要實現(xiàn)一個需求:根據(jù)后臺推送過來的數(shù)據(jù)俊犯,進行動態(tài)頁面跳轉(zhuǎn)妇多,跳轉(zhuǎn)到頁面后根據(jù)返回到數(shù)據(jù)執(zhí)行對應(yīng)的操作。
12.?靜態(tài)庫的原理是什么燕侠?你有沒有自己寫過靜態(tài)編譯庫砌梆,遇到了哪些問題?
靜態(tài)庫是閉源庫贬循,不公開源代碼,都是編譯后的二進制文件桃序,不暴露具體實現(xiàn)杖虾。靜態(tài)庫 一般都是以 .a 或者 .framework 形式存
在。
靜態(tài)庫編譯的文件比較大媒熊,因為整個函數(shù)庫的數(shù)據(jù)都會被整合到代碼中奇适,這樣的好處就是編譯后的程序不需要外部的函數(shù)庫支
持,不好的一點就是如果改變靜態(tài)函數(shù)庫芦鳍,就需要程序重新編譯嚷往。多次使用就有多份冗余拷貝。
使用靜態(tài)庫的好處:模塊化分工合作柠衅、可重用皮仁、避免少量改動導(dǎo)致大量的重復(fù)編譯鏈接。
framework中用到了NSClassFromString菲宴,但是轉(zhuǎn)換出來的class 一直為nil贷祈。
解決方法:在主工程的【Other Linker Flags】需要添加參數(shù)【-ObjC]即可。
如果Xcode找不到框架的頭文件喝峦,你可能是忘記將它們聲明為public了势誊。
?解決方法:進入target的Build Phases頁,展開Copy Headers項谣蠢,把需要public的頭文件從Project或Private部分拖拽到Public部分粟耻。
盡量不要用 xib 查近。由于靜態(tài)框架采用靜態(tài)鏈接,
新建項目挤忙,選擇Cocoa Touch Static Library? ?定義一個類方法+ (void)test;霜威,在.h文件暴露出來
然后選擇Build phases 清空copy Files 下面的Subpath
適配最低版本?設(shè)置支持多個架構(gòu)的的靜態(tài)庫??Build Active Architecture Only?設(shè)置為NO
分別在真機和模擬器下編譯 Debug 和 Release
合并.framework?靜態(tài)庫,合成的是二進制文件而不是framework,最后合成的二進制文件替代之前的二進制文件即可
cd 到 Products目錄饭玲,輸入命令(如果是Debug模式侥祭,將Release替換成Debug即可)lipo-create Release-iphoneos/MXFrameworkTool.framework/MXFrameworkTool Release-iphonesimulator/MXFrameworkTool.framework/MXFrameworkTool-output MXFrameworkTool
13.ViewController生命周期?
nitWithCoder:(如果連接了串聯(lián)圖storyBoard會走這個方法)或initWithNibName:Bundle:(非storyBoard(Xib或純代碼)都會走這個方法)
init
初始化對象 純代碼和Xib都會走這個方法,storyBoard不會
awakeFromNib
此方法被調(diào)用時茄厘,所有視圖的outlet和action已經(jīng)連接矮冬,但還沒有被確定。這個方法可以算作是和視圖控制器的實例化配合在一
起使用的次哈,因為有些需要根據(jù)用戶喜好來進行設(shè)置的內(nèi)容胎署,無法存在storyboard中,所以可以在awakeFromNib方法中被加載進
來窑滞。
loadView
對Controller的View進行初始化
viewDidLoad
視圖加載完成琼牧,但是還沒有從屏幕上顯示出來,可以重寫這個方法哀卫,做一些其它的初始化操作巨坊,比如移除一些視圖,修改約
束此改,加載數(shù)據(jù)等操作
viewWillAppear
在視圖即將顯示到屏幕上的時候調(diào)用趾撵,可以在這個方法里改變當(dāng)前屏幕的方向或狀態(tài)欄風(fēng)格
iewWillLayoutSubViews
這個方法會在控制器將要布局View的子控件時調(diào)用,每當(dāng)視圖的bounds改變時共啃,view將調(diào)整其子控件的位置
viewDidLayoutSubviews
此方法會在控制器已經(jīng)布局子控件的時候調(diào)用
viewDidAppear
此方法在視圖已經(jīng)顯示在屏幕上的時候調(diào)用占调,可以做一些對視圖展示效果的改變
viewWillDisAppear
視圖即將消失,被覆蓋或被隱藏的時候調(diào)用
viewDidDisAppear
視圖已經(jīng)消失移剪,被覆蓋或被隱藏的時候調(diào)用
didReceiveMemoryWarning
當(dāng)系統(tǒng)發(fā)出內(nèi)存警告時究珊,會自動清理視圖,同時系統(tǒng)會調(diào)用此方法來通知視圖控制器
可以在此方法內(nèi)釋放一些資源纵苛,但通常不需要剿涮,因為比較占資源的view已經(jīng)被清除了
14.?輪播圖項目實現(xiàn)細(xì)節(jié)?
第一種:基于collectionView進行的封裝(推薦)
使用UICollectionView的復(fù)用特性,復(fù)制 多份 圖片作為數(shù)據(jù)源,從中間開始顯示
在 第一張圖片前加 上最后一張圖片,在最后一張圖片前加上第一張圖片
注意:
記得設(shè)置scrollView.isPagingEnabled = true
控制UIScrollView回滾的時候 不要使用動畫,否則會調(diào)用代理方法
第二種:基于scrollView的無限輪播(首尾各多創(chuàng)建一個展示圖片的ImageView)
第三種:同樣是基于scrollView的無限輪播(總共就創(chuàng)建三個ImageView)
15.?單行多個Label,中間可壓縮赶站,怎么添加約束幔虏?
[self.label1 setContentHuggingPriority:1forAxis:UILayoutConstraintAxisHorizontal];
[self.label1 setContentCompressionResistancePriority:2forAxis:UILayoutConstraintAxisHorizontal];