11、Objective-C與C、C++之間的聯(lián)系和區(qū)別?
OC
- OC是C的超集,擴展了C語言使它具備面向對象設計的能力。例如類、消息、繼承;同時在OC的代碼中可以有C和C++語句,它可以調用C的函數(shù),也可以通過C++對象訪問方法;
- OC不同于C++,盡管都有面向對象的能力,但他們分屬不同的學派,OC屬于SmallTalk學派,C++屬于Simula 67學派(一種早期的面向對象語言)。
- OC可以底層系統(tǒng)編程,另一方面可以支持利用動態(tài)架構進行開發(fā)。
OC于C++的比較
兩者都是面向對象設計語言,有很多相似之處,但屬于不同的學派,也有不同之處:
- 繼承:OC不支持多繼承,C++支持多繼承
- 函數(shù)調用:OC通過傳遞消息實現(xiàn)函數(shù)調用,而C++直接進行函數(shù)調用
- 定型:OC是動態(tài)類型,所以它的類庫比C++容易操作。OC在運行時可以允許根據(jù)字符串名字訪問方法和類,還可以動態(tài)鏈接和添加庫。而C++,對象的靜態(tài)類型決定你是否可以發(fā)消息給它。
- 接口:OC采用協(xié)議(正式和非正式)的形式定義接口,而C++采用虛函數(shù)的形式定義接口
- 方法重載:OC不支持方法重載,C++支持方法重載。方法重載就是方法名相同,參數(shù)個數(shù)相同,但是參數(shù)類型不同或者返回值不同。
兩者的主要差別是因為OC即支持動態(tài)類型也支持靜態(tài)類型。對于id類型的變量,變量只是一個容器,本身是沒有類型的,或者只是屬于最基本的類型,所以也不需要強制類型轉換。因為編譯器不會檢查變量類型是否正確,只是運行時如果類型不正確才會產(chǎn)生異常。
而C++是靜態(tài)語言,編譯時會檢查類型,所以必須要加上強制類型轉換,否則編譯器就會報錯。
12、UICollectionView自定義layout如何實現(xiàn)?
- 創(chuàng)建UICollectionViewLayout子類
- 創(chuàng)建UICollectionViewLayoutAttributes子類,用于描述每個cell的size,frame,transform屬性。
- 重寫prepare方法,并在方法內(nèi)部實現(xiàn)各個cell的size、center等的計算。
- 重寫layoutAttributesForElements和layoutAttributesForItem方法,方法內(nèi)部返回UICollectionViewLayoutAttributes實例用于對cell進行布局。
- 如果有自定義attribute屬性,可以放在UICollectionViewLayoutAttributes類中,并在cell實現(xiàn)類的applyLayoutAttributes方法中為每個cell進行屬性賦值。
13、進程和線程的區(qū)別?同步異步的區(qū)別?并行和并發(fā)的區(qū)別?
進程和線程
進程:一個正在運行的程序可以看做一個進程。(例如:正在運行的QQ就是一個進程),進程擁有獨立運行所需的全部資源。
線程:程序中獨立運行的代碼段。(例如:接收QQ消息的代碼)
一個進程是由一或多個線程組成。進程只負責資源的調度和分配,線程才是程序真正的執(zhí)行單元,負責代碼的執(zhí)行。同步和異步
同步是指:當程序1調用程序2時,程序1停下不動,直到程序2完成回到程序1,程序1才繼續(xù)執(zhí)行下去。
異步是指:當程序1調用程序2時,程序1徑直繼續(xù)自己的下一個動作,不受程序2的影響。
或者
同步是指:發(fā)送方發(fā)出數(shù)據(jù)后,等接受方發(fā)揮響應以后才發(fā)下一個數(shù)據(jù)包的通信方式
異步是指:發(fā)送方發(fā)出數(shù)據(jù)后,不等接收方發(fā)回響應,接著發(fā)送下一個數(shù)據(jù)包的通訊方式。-
并行和并發(fā)
并行(parallel):同一時刻可以互不干擾的同時做幾件事
并行.png
并發(fā)(concurrency):統(tǒng)一時間段做幾件事

并發(fā)的解決方案:
- 隊列和緩沖區(qū)
- 爭搶鎖資源
- 預處理
- 并行
- 提速
- 消息中間件(RabbitMQ)
參考鏈接
http://www.itdecent.cn/p/c334f8198f9b
14、線程間通信?
線程間通信的體現(xiàn):
一個線程傳遞數(shù)據(jù)給另一個線程
在一個線程中執(zhí)行玩特定任務后,轉到另一個線程繼續(xù)執(zhí)行任務
常用的線程間通信的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;作者:Smallwolf_JS
15、GCD的一些常用的函數(shù)?(group,barrier,信號量,線程同步)
GCD的使用很簡單:
- 創(chuàng)建一個隊列(串行隊列和并行隊列)
- 將任務追加到等待隊列中,然后系統(tǒng)就會根據(jù)任務的類型執(zhí)行(同步執(zhí)行和異步執(zhí)行)
dispatch_queue_create
dispatch_barrier_async
dispatch_after
dispatch_once
dispatch_apply
dispatch_group
dispatch_group_notify
dispatch_group_wait
dispatch_group_enter
dispatch_group_leave
信號量的作用在于處理多線程任務訪問資源限制的問題,有時候也用于上鎖和解鎖的問題。
dispatch_semaphore_create
dispatch_semaphore_signal 增加信號量
dispatch_semaphore_wait 減少信號量
/**
* 線程安全:使用 semaphore 加鎖
* 初始化火車票數(shù)量、賣票窗口(線程安全)、并開始賣票
*/
- (void)initTicketStatusSave {
NSLog(@"currentThread---%@", [NSThread currentThread]);
NSLog(@"semaphore---begin");
self.semaphoreLock = dispatch_semaphore_create(1);
self.ticketSurplusCount = 50;
dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testqueue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testqueue2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
}
/**
* 售賣火車票(線程安全)
*/
- (void)saleTicketSafe {
while (1) {
//相當于加鎖
dispatch_semaphore_wait(self.semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) {
self.ticketSurplusCount--;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%zd 窗口: %@", self.ticketSurplusCount, [NSThread currentThread]]);
[NSThread sleepForTimeInterval:0.2];
} else {
NSLog(@"所有火車票已售完");
dispatch_semaphore_signal(self.semaphoreLock);
break;
}
//相當于解鎖
dispatch_semaphore_signal(self.semaphoreLock);
}
}
參考鏈接:
http://www.itdecent.cn/p/2d57c72016c6
http://www.itdecent.cn/p/189a09d669de
16、如何訪問并修改一個類的私有屬性?
訪問以及修改私有屬性的兩種方式:
KVC
runtime
objc_msgSend() 通過私有屬性的setter和getter方法實現(xiàn)
KVC
setValue:forKey:
valueForKey:
-(void)way1{
PrivateVariablesClass *classA = [[PrivateVariablesClass alloc] init];
[classA setValue:@(4) forKey:@"_priviteNum"];
[classA setValue:self.view forKey:@"_priviteView"];
[classA showPropertyPrivateVariablesClass];
}
runtime
-(void)way2{
PrivateVariablesClass *classA = [[PrivateVariablesClass alloc] init];
unsigned int outCount = 0;
Ivar *ivars = class_copyIvarList([PrivateVariablesClass class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
const char *ivarName = ivar_getName(ivar);
//這里要注意ARC下, 這個會報錯
/**
在修改NSInteger型變量的時候,ARC下,編譯器不允許你將NSInteger類型的值賦值給id,在buildsetting中將Objective-C Automatic Reference Counting修改為No即可。但是這樣工程就會變成MRC,所以,如果是非對象類型就不建議用object_setIvar這樣的方法去修改了。
*/
int a = strcmp(ivarName, "_priviteNum");
if (strcmp(ivarName, "_priviteNum") == 0) {
//這種方式傳值int類型會報錯,不能傳入
object_setIvar(classA, ivar, 22);
}
if (strcmp(ivarName, "_priviteView") == 0) {
object_setIvar(classA, ivar, self.view);
}
}
[classA showPropertyPrivateVariablesClass];
}
使用ivar_getName()獲取屬性名并使用object_setIvar()修改屬性值,使用獲取的屬性名通過valueForKey:修改屬性值
objc_msgSend()
-(void)way3{
PrivateVariablesClass *classA = [[PrivateVariablesClass alloc] init];
((void (*)(id, SEL, int))(void *) objc_msgSend)((id)classA, @selector(setPriviteNum:) , 33);
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)classA, @selector(setPriviteView:) , self.view);
[classA showPropertyPrivateVariablesClass];
}
17、數(shù)據(jù)持久化的幾個方案(fmdb用沒用過)
- plist文件(屬性列表)
- preference(偏好設置)
- NSKeyedArchiver(歸檔)
- SQLite 3
- CoreData
FMDB的三個主要的類:
- FMDatabase
一個FMDatabase對象就代表一個單獨的SQLite數(shù)據(jù)庫,用來執(zhí)行SQL語句 - FMResultSet
使用FMResultSet執(zhí)行查詢后的結果集 - FMDatabaseQueue
用于在多線程中執(zhí)行多個查詢和更新,它是線程安全的
CoreData
iOS10以后使用NSPersistentContainer來管理數(shù)據(jù)庫對象。NSPersistentContainer中包括NSManagedObjectContext、NSManagedObjectModel、NSPersistentStoreCoordinator。
不是太復雜的模型修改,CoreData都可以自動遷移數(shù)據(jù),我們只需要重新生成table映射的model文件即可。
數(shù)據(jù)庫文件默認存放在Library/Application Support文件夾中
參考鏈接
http://www.itdecent.cn/p/7616cbd72845
http://www.itdecent.cn/p/6e048f7c5812
18、說一下AppDelegate的幾個方法?從后臺到前臺調用了哪些方法?第一次啟動調用了哪些方法?從前臺到后臺調用了哪些方法?
- 應用啟動,并進行初始化時調用:
aaplication:didFimnishLanuchingWithOptions: - 應用進入前臺并處于活動狀態(tài)時候調用:
applicationDidBecomeActive: - 應用從活動狀態(tài)進入到非活動狀態(tài):
applicationWillResignActive: - 應用進入到后臺時候調用的方法:
applicationDidEnterBackground: - 應用進入到前臺時候調用的方法:
appplicationWillEnterForeground: - 應用被終止的狀態(tài):
applicationWillTeminate:
從后臺到前臺調用了哪些方法?
appplicationWillEnterForeground:
applicationDidBecomeActive:
第一次啟動調用了哪些方法?
aaplication:didFimnishLanuchingWithOptions:
applicationDidBecomeActive:
從前臺到后臺調用了哪些方法?
applicationWillResignActive:
applicationDidEnterBackground:
19、NSCache優(yōu)于NSDictionary的幾點?
- NSCache具有自動刪除的功能,以減少系統(tǒng)占用的內(nèi)存
- NSCache是線程安全的,不需要加線程鎖;
- 鍵對象不會像NSMutableDictionary中那樣被復制。(NSCache的key只是對對象的strong引用,對象不需要實現(xiàn)NSCopying協(xié)議,NSCache也不會像NSDictionary一樣復制對象)。
20、知不知道Designated Initializer?使用它的時候有什么需要注意的問題?
指定初始化函數(shù) vs 便利初始化函數(shù)
-
子類如果有指定初始化函數(shù),那么指定初始化函數(shù)實現(xiàn)時必須調用它的直接父類的指定初始化函數(shù)。
image.png -
子類如果有指定初始化函數(shù),那么便利初始化函數(shù)必須調用自己的其他初始化函數(shù)(包括指定初始化函數(shù)以及其他的便利初始化函數(shù)),不能調用super的初始化函數(shù)。
image.png -
如果子類提供了指定初始化函數(shù),那么一定要實現(xiàn)所有父類的指定初始化函數(shù)。
image.png

當 initWithCoder: 遇到 NS_DESIGNATED_INITIALIZER
- 如果父類沒有實現(xiàn)NSCoding協(xié)議,那么應該調用父類的指定初始化函數(shù)
- 如果父類實現(xiàn)了NSCoding協(xié)議,那么子類的initWithCoder:的實現(xiàn)中需要調用父類的initWithCoder:方法
實現(xiàn)NSCoding協(xié)議的時候,我們可以顯示的聲明 initWithCoder: 為指定初始化函數(shù)(一個類可以有多個指定初始化函數(shù),比如UIViewController)即可完美解決問題,既滿足了指定初始化函數(shù)的三個規(guī)則,又滿足了NSCoding協(xié)議的三條原則。
總計歸納
- 便利初始化函數(shù)只能調用自己類中的其他初始化方法
- 指定初始化函數(shù)才有資格調用父類的指定初始化函數(shù)

參考文章
https://www.cnblogs.com/smileEvday/p/designated_initializer.html
21、實現(xiàn)description方法能取到什么效果?
NSLog(@"%@",object);
調試的時候方便打印對自己有用的信息
22、objc使用什么機制管理對象內(nèi)存?
ARC自動引用計數(shù)



