BEGAN
1聊浅、如何正確使用const餐抢,static, extern现使?
作用:
const:限制類型(1低匙、僅僅用來修飾右邊的變量;2碳锈、被 const 修飾的變量只能是只讀的)
static:
1顽冶、修飾局部變量:
1)延長局部變量的生命周期,程序結束才會銷毀售碳;
2)局部變量只能生成一份內存强重,只會初始化一次;
3)改變局部變量的作用域贸人;
2间景、修飾全局變量:
1)只能在本文件中訪問,修改全局變量的作用域艺智,生命周期不會改倘要;
2)避免重復定義全局變量
extern:用來獲取全局變量的值(包括靜態(tài)的全局變量)
延伸:
1、const 與宏的區(qū)別十拣?
1)編譯時刻:宏是預編譯封拧,const 是編譯階段
2)編譯檢查:宏只是替換,在編譯時不會報錯夭问,不檢查泽西,const 會編譯檢查,會報錯
3)宏的好處:宏能定義一些函數缰趋、方法捧杉,const 不能
4)宏的壞處:大量的宏會導致編譯時間變長陕见,每次都要重新替換
2、const 與 static 配合使用
在多個文件中使用同一個字符串常量,可以使用 const 與 static 組合韩脑;
const 與 static 組合:在每個文件都需要定義一份靜態(tài)全局變量
extern 與 static 組合:只需要定義一次全局變量陵珍,多個文件共享
2、@property 關鍵字詳解蜕着,assign 與weak、 __block 與 __weak红柱、strong 與copy的區(qū)別承匣?
1、assign 與 weak 區(qū)別:
assign 適用于基本數據類型锤悄,基礎數據類型一般分配在棧上韧骗,棧的內存用系統自動處理。
weak 適用于修飾 NSObject 對象零聚,是一個弱引用袍暴。一般修飾代理和 IB 控件。
2隶症、strong 與copy的區(qū)別:
strong 與 copy 都會使引用計數加1政模,但是 strong 是兩個指針指向統一個內存地址,copy 會在內存里拷貝一份對象蚂会,兩個指針指向不用的內存地址淋样。
3、__weak與__block的區(qū)別:
1)使用 __block修飾的變量在block代碼塊中會被retain(ARC下會retain胁住,MRC下不會retain)
2)使用__weak修飾的變量不會在block代碼塊中被retain
要避免block出現循環(huán)引用 __weak __typedof(&*self)weakSelf = self;
4趁猴、block變量定義時為什么用copy?block 是放在哪里的彪见?
block本身是像對象一樣可以retain儡司,和release。但是余指,block在創(chuàng)建的時候捕犬,它的內存是分配在棧(stack)上,可能被隨時回收浪规,而不是在堆(heap)上或听。通過copy可以把block拷貝(copy)到堆,保證block的聲明域外使用
3笋婿、一個按鈕被一個半透明的View部分遮擋,需要點擊到按鈕的時候,按鈕始終響應
// 一個View超出了父視圖的范圍,需要點擊超出范圍的View也有響應
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
//當觸摸點在按鈕上的時候,才讓按鈕去響應事件.
//把當前點轉換成按鈕坐標系上的點.
CGPoint btnP = [self convertPoint:point toView:self.btn];
if ( [self.btn pointInside:btnP withEvent:event]) {
return self.btn;
}else{
return [super hitTest:point withEvent:event];
}
}
4誉裆、iOS 的沙盒目錄結構是怎樣的?
Application:存放程序源文件缸濒,上架前經過數字簽名足丢,上架后不可修改
Documents:常用目錄粱腻,iCloud備份目錄,存放數據,這里不能存緩存文件,否則上架不被通過
Library :
Caches:存放體積大又不需要備份的數據,SDWebImage緩存路徑就是這個
Preference:設置目錄斩跌,iCloud會備份設置信息
tmp:存放臨時文件绍些,不會被備份,而且這個文件下的數據有可能隨時被清除的可能
5耀鸦、+load 和 +initialize 的區(qū)別是什么柬批?
+(void)load;
當類對象被引入項目時, runtime 會向每一個類對象發(fā)送 load 消息 load 方法會在每一個類甚至分類被引入時僅調用一次,調用的順序:父類優(yōu)先于子類, 子類優(yōu)先于分類
load 方法不會被類自動繼承
+(void)initialize;
也是在第一次使用這個類的時候會調用這個方法
6、如何讓 Category 支持屬性袖订? runtime實現
頭文件
@interface NSObject (test)
@property (nonatomic, copy) NSString *name;
@end
.m文件
@implementation NSObject (test)
// 定義關聯的key
static const char *key = "name";
- (NSString *)name {
// 根據關聯的key氮帐,獲取關聯的值。
return objc_getAssociatedObject(self, key);
}
- (void)setName:(NSString *)name {
// 第一個參數:給哪個對象添加關聯
// 第二個參數:關聯的key洛姑,通過這個key獲取
// 第三個參數:關聯的value
// 第四個參數:關聯的策略
objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
7上沐、strong / weak / unsafe_unretained 的區(qū)別?
? weak只能修飾OC對象,使用weak不會使計數器加1,對象銷毀時修飾的對象會指向nil
? strong等價與retain,能使計數器加1,且不能用來修飾數據類型
? unsafe_unretained等價與assign,可以用來修飾數據類型和OC對象,但是不會使計數器加1楞艾,且對象銷毀時也不會將對象指向nil,容易造成野指針錯誤
8参咙、frame 和 bounds 的區(qū)別是什么?
(1)frame相對于父視圖,是父視圖坐標系下的位置和大小硫眯。bounds相對于自身,是自身坐標系下的位置和大小蕴侧。
(2)frame以父控件的左上角為坐標原點,bounds以自身的左上角為坐標原點
9舟铜、KVO與Notification的異同
KVO和Notification本質都是觀察者模式戈盈。
KVO是被觀察者直接發(fā)消息(-willChange和-didChange)奠衔,耦合性較強谆刨,適合某些綁定,比如說界面上的進度條顯示;
Notification是被觀察者發(fā)消息給NotificationCenter归斤,再由NotificationCenter轉發(fā)出去痊夭,耦合性較低,適合登錄脏里、等級變化她我、監(jiān)聽全局的某個屬性變化;
10迫横、Objective-C消息機制的原理
Objective-C的類結構
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
typedef struct objc_class *Class;
objc_msgSend方法:objc_msgSend含兩個必要參數:receiver番舆、方法名(selector)
[receiver message];將被轉換為:objc_msgSend(receiver, selector);
帶參數的情況是:objc_msgSend(receiver, selector, arg1, arg2, …);
當向一個對象發(fā)送消息時,objc_msgSend方法根據對象的isa指針找到對象的原來類矾踱,然后在類的方法列表中查找selector恨狈;
如果查找不到,通過Class super_class指針找到父類呛讲,并在父類的方法列表查找禾怠,直到NSObject類返奉;
11、數據的持久化
ios中存儲數據基本上就是plist吗氏、sqlite和CoreData (NSUserDefault其實也是plist)
常見的持久化實現:
1)實現NSCoding芽偏,配合runtime讀取屬性,再用NSKeyedArchiver存儲到文件中弦讽;
2)實現NSCoding污尉,存儲到NSUserDefault;
3)數據庫往产,使用SQLitePersistentObjects寫入db十厢;
4)使用CoreData;
12捂齐、聊天室中UITableView的優(yōu)化
每一條消息是單獨的UITableViewCell蛮放,通過富文本顯示聊天消息,耗時操作是:富文本拼接奠宜、高度計算包颁、滾動顯示;
業(yè)務方向:
(1)下發(fā)房間配置文件压真,房間分普通娩嚼、熱鬧、火爆等狀態(tài)滴肿,某些情況下省略不必要的消息岳悟,再進行發(fā)言等級控制等;
(2)消息合并泼差,對同類型的消息進行合并贵少;
代碼方向:
(1)根據幀率動態(tài)加載消息數量,當進行消息追趕的時候堆缘,多條消息調用一次insert滔灶,用CADisplayLink保證添加速率和幀率一致;
(2)代碼創(chuàng)建cell
(3)圖像預加載吼肥,程序在啟動的時候會進行禮物版本同步录平,把禮物圖片預先下載好,在顯示直接通過富文本進行圖片拼接缀皱;(為了避免鋸齒斗这,圖像大小和顯示使用整數)
(4)富文本根據消息內容進行拼接后緩存;
13啤斗、TCP/IP
(1)3次握手-建立連接
1表箭、A發(fā)送sync報文;seq=x Sync=1
2争占、B回復ack報文燃逻;seq=y Sync=1 ack=x+1
3序目、A回復ack報文;seq=x+1 Sync=1 ack=y+1
(2)4次握手-斷開連接
1伯襟、A端發(fā)送FIN猿涨,停止發(fā)送報文;A進入FIN-WAIT
2姆怪、B端發(fā)送ACK叛赚,表示收到,繼續(xù)發(fā)送報文稽揭; A收到報文進入FIN2-WAIT
3俺附、B端發(fā)送FIN,停止發(fā)送報文溪掀;B進入CLOSE_WAIT
4事镣、A端收到FIN,發(fā)送ACK報文揪胃,A進入TIME_WAIT狀態(tài)
14璃哟、HTTP協議
http(超文本傳輸協議)是一個基于請求與響應模式的、無狀態(tài)的喊递、應用層的協議随闪,常基于TCP的連接方式
http請求由三部分組成骚勘,分別是:請求行铐伴、消息報頭、請求正文俏讹。
常見狀態(tài)碼:
200 成功
400 請求的語法錯誤
403 Forbidden
404 not found 服務器找不到請求的資源
408 Request Time out
500 服務器內部錯誤
請求頭
GET 請求方法当宴、地址、協議版本
GET /foo.php?first_name=John&last_name=Doe&action=Submit HTTP/1.1
請求體(POST請求有)
form-data
HTTP響應
HTTP響應也是由三個部分組成藐石,分別是:狀態(tài)行即供、消息報頭定拟、響應正文于微。
15、線程安全問題
線程之間的資源共享青自,本質是對同一對象株依、變量、文件等進行修改和訪問延窜,主要有以下同步方式:
加鎖恋腕;
原子操作;
sync代碼塊逆瑞;
@synchronized( 同一對象){
// 線程執(zhí)行代碼;
}
NSOperationQueue 可以停止隊列還沒執(zhí)行
suspended
但是不能終止當前操作荠藤。
16伙单、NSOperation 相比 GCD 有哪些優(yōu)勢
(1)更容易的添加依賴關系;
(2)提供了任務的狀態(tài):isExecuteing哈肖、isFinished吻育;
(3)可以很方便的取消一個NSOperation的執(zhí)行。
17淤井、如何為 Class 定義一個對外只讀對內可讀寫的屬性?
在頭文件申明屬性的時候用 readonly布疼, 在.m 文件重新申明屬性的時候用 readwrite
18、UIView 和 CALayer 之間的關系币狠?
(1)UIView 繼承自 UIResponder游两,可以響應事件,CALayer不可以響應用戶事件漩绵;
(2)UIView本身贱案,更像是一個CALayer的管理器,訪問它的根繪圖和坐標有關的屬性止吐,如frame轰坊,bounds等,實際上內部都是訪問它所在CALayer的相關屬性祟印。
19肴沫、創(chuàng)建一個單例
+ (XYHundle *)shareHaudle {
static XYHundle *shareHaudle = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareHaudle = [[XYHundle alloc] init];
});
return shareHaudle;
}
20、冒泡排序(或其他算法)
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"10", @"2", @"22", @"14", @"18", nil];
NSLog(@"打印前的數組:%@", array);
for (int i = 0; i < array.count - 1; i++) {
for (int j = 0; j < array.count - 1 - i; j++) {
if ([array[j + 1] intValue] < [array[j] intValue]) {
int temp = [array[i] intValue];
temp = [array[j] intValue];
array[j] = array[j + 1];
array[j + 1] = [NSString stringWithFormat:@"%d",temp];
}
}
}
NSLog(@"打印后的數組:%@", array);
21蕴忆、哪些途徑可以讓 ViewController 瘦下來颤芬?(MVVM 思想)
(1)把 Data Source 和其他 Protocols 分離出來(將UITableView或者UICollectionView的代碼提取出來放在其他類中)
(2)將業(yè)務邏輯移到 Model 中(和模型有關的邏輯全部在model中寫)
(3)把網絡請求邏輯移到 Model 層(網絡請求依靠模型)
(4)把 View 代碼移到 View 層(自定義View)
22、有哪些常見的 Crash 場景套鹅?
(1)訪問了僵尸對象
(2)訪問不存在的方法
(3)數組越界
(4)在定時器下一次回調前將定時器釋放,會Crash
23站蝠、如果一個函數10次中有7次正確,3次錯誤卓鹿,問題可能出現在哪里菱魔?
1.首先既然有正確有錯誤,那么這個bug肯定是不一定會出錯的吟孙,先看函數條件是否有漏寫澜倦;
2.然后再檢查函數是否會存在空的情況;
3.反復操作以上步驟去查明每個調用的函數結果都是正確的杰妓。
24藻治、iOS開發(fā)中Debug和Release的區(qū)別
Debug : 調試版本,主要是讓程序員使用,在調試的過程中調用 Debug 會啟動更多的服務來監(jiān)控錯誤,運行速度相對較慢,而且比較耗能.
Release : 發(fā)布版本,主要是讓用戶使用, 在使用的過程中會去掉那些繁瑣的監(jiān)控服務,運行速度相對較快,而且比較節(jié)約內存.
25、請寫一個”標準"宏MIN 巷挥,這個宏輸入兩個參數并返回較小的一個桩卵。
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
26、請分別說明@public、@protected雏节、@private的含義與作用
@public:對象的實例變量的作用域在任意地方都可以被訪問 胜嗓;
@protected:對象的實例變量作用域在本類和子類都可以被訪問 ;
@private:實例變量的作用域只能在本類(自身)中訪問 钩乍。
27兼蕊、@synthesize、@dynamic的區(qū)別
@synthesize是系統自動生成getter和setter屬性聲明;
@synthesize的意思是件蚕,除非開發(fā)人員已經做了孙技,否則由編譯器生成相應的代碼,以滿足屬性聲明排作;
@dynamic是開發(fā)者自已提供相應的屬性聲明;
@dynamic意思是由開發(fā)人員提供相應的代碼:對于只讀屬性需要提供getter牵啦,對于讀寫屬性需要提供 setter 和getter。
28妄痪、block使用時的注意點哈雏?
1.在block內部使用外部指針且會造成循環(huán)引用情況下,需要用weak修飾外部指針weak typeof(self) weakSelf = self;
2.在block內部如果調用了延時函數還使用弱指針會取不到該指針,因為已經被銷毀了,需要在block內部再將弱指針重新強引用一下__strong typeof(self) strongSelf = weakSelf;
3.如果需要在block內部改變外部變量的話,需要在用__block修飾外部變量
舉個??
// 創(chuàng)建一個 Student 類
#import <Foundation/Foundation.h>
@interface Student : NSObject
@property(nonatomic, strong) NSString *name;
@end
// Student.m
#import "Student.h"
@implementation Student
@end
隨便在一個函數中調用,例如下面的函數中:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Student *student = [[Student alloc] init];
student.name = @"Tom";
__weak typeof(Student) *weakSelf = student;
void(^block)()=^{
NSLog(@"Student Name = %@",weakSelf.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Student Name(Delay) = %@",weakSelf.name);
});
};
block();
}
打印出來的信息:
2017-02-23 10:55:03.876 TestBlock[1904:336958] Student Name = Tom
2017-02-23 10:55:05.876 TestBlock[1904:337002] Student Name(Delay) = (null)
dispatch_after函數在viewDidLoad函數結束后執(zhí)行衫生,此時student對象已經被銷毀裳瘪,所以weakSelf所引用的內容已經不存在,所以取得不到Student Name罪针。
因此對于Block內部的延時函數彭羹,為了保證延時之后Block所引用的對象還存在,需要用__strongSelf引用泪酱。上面的代碼修改為:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
Student *student = [[Student alloc] init];
student.name = @"Tom";
__weak typeof(Student) *weakSelf = student;
void(^block)()=^{
// 需要強引用下
__strong typeof(Student) *strongSelf = weakSelf;
NSLog(@"Student Name = %@",strongSelf.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Student Name(Delay) = %@",strongSelf.name);
});
};
block();
}
29派殷、[@property (nonatomic, copy) NSString *name; 重寫 setter 方法]
- (void)setName:(NSString *)name {
_name = [name copy];
}
[@property (nonatomic, retain) NSString *name; 重寫 setter 方法]
– (void) setName:(NSString*) str {
[str retain];
[name release];
name = str;
}
——————————底層實現————————————
1、字典轉模型之MJExtension底層實現
//返回一個創(chuàng)建好的模型
+ (instancetype)modelWithDict:(NSDictionary *)dict
{
//創(chuàng)建一個模型
id objc = [[self alloc] init];
int count = 0;
/*
方法:獲取成員變量列表
參數一:class獲取哪個類成員變量列表
參數二:count成員變量總數
*/
// 成員變量數組 指向數組第0個元素
Ivar *ivarList = class_copyIvarList(self, &count);
// 遍歷所有成員變量
for (int i = 0; i < count; i++) {
// 獲取成員變量
Ivar ivar = ivarList[i];
// 獲取成員變量名稱(將c轉為oc)
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
// 成員變量名稱轉換key(將成員變量前邊的"_"截取掉)
NSString *key = [ivarName substringFromIndex:1];
// 從字典中取出對應value
id value = dict[key];
// 給模型中屬性賦值(底層會去找對應的屬性和值)
[objc setValue:value forKey:key];
}
return objc;
}
2墓阀、無限輪播實現原理
UIScrollView
1毡惜、準備好一個UIScrollView,上面依次放上去三個UIImageView斯撮,簡稱為A经伙,B,C勿锅。
2帕膜、假設我們要放4張圖片,那么首先我們在A B C上依次放的是:A -> 最后一張圖片, B -> 第一張圖片, C -> 第二張圖片粱甫。首先讓ScrollView顯示B也就是第一張圖片泳叠。
(1)當我們左滑顯示第二張圖片也就是C,當頁面顯示滑動停止以后茶宵,我們將A B C重新顯示圖片為:A -> 第一張圖片, B -> 第二張圖片, C -> 第三張圖片,然后讓ScrollView再次顯示B宗挥。所以界面顯示的圖片雖然變了乌庶,但一直顯示的還是中間那個UIImageView种蝶。這樣就造成了無限循環(huán)滑動。右滑原理一樣瞒大。
(2)如果我們右滑顯示最后一張圖片也就是A螃征,當頁面顯示滑動停止以后,我們將A B C重新顯示圖片為:A -> 倒數第二張圖片, B -> 最后一張圖片, C -> 第一張圖片透敌,然后讓ScrollView再次顯示B盯滚。這樣子就是一個無限循環(huán)輪播。
func reloadImage() {
let currentIndex = pageView.currentPage
let nextIndex = (currentIndex + 1) % 4
let preIndex = (currentIndex + 3) % 4
(scrollView.subviews[0] as! UIImageView).image = UIImage(named: "\(preIndex).png")
(scrollView.subviews[1] as! UIImageView).image = UIImage(named: "\(currentIndex).png")
(scrollView.subviews[2] as! UIImageView).image = UIImage(named: "\(nextIndex).png")
}
UICollectionView
1酗电、設置UICollectionView的cell為兩倍image數量魄藕,然后循環(huán)對cell從第一張圖片到最后一張圖片賦值,首先顯示第二部分的第一張圖片撵术。
2背率、當左滑顯示到第二部分的最后一張圖片的時候,也就是最后一個cell嫩与,當圖片顯示完成以后寝姿,我們將scrollView設置為顯示第一部分的最后一張圖片,這樣子就可以繼續(xù)右滑划滋,就是無限循環(huán)輪播的效果饵筑。
3、當右滑顯示到第一部分的第一張圖片的時候处坪,也就是第一個cell翻翩,當圖片顯示完成以后,我們將scrollView設置為顯示第二部分的第一張圖片稻薇,這樣子就可以繼續(xù)左滑嫂冻,就是無限循環(huán)輪播的效果。
func reloadImage() {
guard let currentIndexPath = currentIndexPath else {
return
}
if currentIndexPath.item == images.count * 2 - 1 { //如果是最后一個圖片塞椎,回到第一部分的最后一張圖片
let newIndexPath = IndexPath(item: images.count - 1, section: 0)
self.currentIndexPath = newIndexPath
collectionView.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: false)
} else if currentIndexPath.item == 0 { //如果是第一個圖片桨仿,就回到第二部分的第一張圖片
let newIndexPath = IndexPath(item: images.count, section: 0)
self.currentIndexPath = newIndexPath
collectionView.scrollToItem(at: newIndexPath, at: .centeredHorizontally, animated: false)
}
}
后續(xù)持續(xù)更新~
END