iOS最新面試題匯總:
iOS最新面試題匯總(一)
iOS最新面試題匯總(二)
iOS最新面試題匯總(三)
iOS最新面試題匯總(四)
-
Runtime實現的機制是什么,怎么用,一般用于干嘛?
1). 使用時需要導入的頭文件 <objc/message.h> <objc/runtime.h>
2). Runtime 運行時機制,它是一套C語言庫。
3). 實際上我們編寫的所有OC代碼,最終都是轉成了runtime庫的東西。
比如:
類轉成了 Runtime 庫里面的結構體等數據類型,
方法轉成了 Runtime 庫里面的C語言函數,
平時調方法都是轉成了 objc_msgSend 函數(所以說OC有個消息發送機制)
// OC是動態語言,每個方法在運行時會被動態轉為消息發送,即:objc_msgSend(receiver, selector)。
// [stu show]; 在objc動態編譯時,會被轉意為:objc_msgSend(stu, @selector(show));
4). 因此,可以說 Runtime 是OC的底層實現,是OC的幕后執行者。
-
有了Runtime庫,能做什么事情呢?
Runtime庫里面包含了跟類、成員變量、方法相關的API。
比如:
(1)獲取類里面的所有成員變量。
(2)為類動態添加成員變量。
(3)動態改變類的方法實現。
(4)為類動態添加新的方法等。
因此,有了Runtime,想怎么改就怎么改。
-
什么是 Method Swizzle(黑魔法),什么情況下會使用?
1). 在沒有一個類的實現源碼的情況下,想改變其中一個方法的實現,除了繼承它重寫、和借助類別重名方法暴力搶先之外,還有更加靈活的方法 Method Swizzle。
2). Method Swizzle 指的是改變一個已存在的選擇器對應的實現的過程。OC中方法的調用能夠在運行時通過改變,通過改變類的調度表中選擇器到最終函數間的映射關系。
3). 在OC中調用一個方法,其實是向一個對象發送消息,查找消息的唯一依據是selector的名字。利用OC的動態特性,可以實現在運行時偷換selector對應的方法實現。
4). 每個類都有一個方法列表,存放著selector的名字和方法實現的映射關系。IMP有點類似函數指針,指向具體的方法實現。
5). 我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP。
6). 我們可以利用 class_replaceMethod 來修改類。
7). 我們可以利用 method_setImplementation 來直接設置某個方法的IMP。
8). 歸根結底,都是偷換了selector的IMP。
-
_objc_msgForward 函數是做什么的,直接調用它將會發生什么?
答:_objc_msgForward是 IMP 類型,用于消息轉發的:當向一個對象發送一條消息,但它并沒有實現的時候,_objc_msgForward會嘗試做消息轉發。
-
什么是 TCP / UDP ?
TCP:傳輸控制協議。
UDP:用戶數據協議。
TCP 是面向連接的,建立連接需要經歷三次握手,是可靠的傳輸層協議。
UDP 是面向無連接的,數據傳輸是不可靠的,它只管發,不管收不收得到。
簡單的說,TCP注重數據安全,而UDP數據傳輸快點,但安全性一般。
通信底層原理(OSI七層模型)
OSI采用了分層的結構化技術,共分七層:
物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層。
-
介紹一下XMPP?
XMPP是一種以XML為基礎的開放式實時通信協議。
簡單的說,XMPP就是一種協議,一種規定。就是說,在網絡上傳東西,XMPP就是規定你上傳大小的格式。
-
OC中創建線程的方法是什么?如果在主線程中執行代碼,方法是什么?
// 創建線程的方法
- [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];
// 主線程中執行代碼的方法
- [self performSelectorOnMainThread:nil withObject:nil waitUntilDone:YES];
- dispatch_async(dispatch_get_main_queue(), ^{});
- [[NSOperationQueue mainQueue] addOperation:nil];
-
tableView的重用機制?
答:UITableView 通過重用單元格來達到節省內存的目的: 通過為每個單元格指定一個重用標識符,即指定了單元格的種類,當屏幕上的單元格滑出屏幕時,系統會把這個單元格添加到重用隊列中,等待被重用,當有新單元格從屏幕外滑入屏幕內時,從重用隊列中找看有沒有可以重用的單元格,如果有,就拿過來用,如果沒有就創建一個來使用。
-
用偽代碼寫一個線程安全的單例模式
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;
}
-
如何實現視圖的變形?
答:通過修改view的 transform 屬性即可。
-
在手勢對象基礎類UIGestureRecognizer的常用子類手勢類型中哪兩個手勢發生后,響應只會執行一次?
答:UITapGestureRecognizer,UISwipeGestureRecognizer是一次性手勢,手勢發生后,響應只會執行一次。
-
字符串常用方法:
NSString *str = @"abc*123";
NSArray *arr = [str componentsSeparatedByString:@"*"]; //以目標字符串把原 字符串分割成兩部分,存到數組中。@[@"abc", @"123"];
-
如何高性能的給 UIImageView 加個圓角?
不好的解決方案:使用下面的方式會強制Core Animation提前渲染屏幕的離屏繪制, 而離屏繪制就會給性能帶來負面影響,會有卡頓的現象出現。
self.view.layer.cornerRadius = 5.0f;
self.view.layer.masksToBounds = YES;
正確的解決方案:使用繪圖技術
- (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();
// 關閉上下文
UIGraphicsEndImageContext();
return image;
}
還有一種方案:使用了貝塞爾曲線"切割"個這個圖片, 給UIImageView 添加了的圓角,其實也是通過繪圖技術來實現的。
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相關的模型,然后將模型數據傳給view,通過模型上的數據給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協議中 POST 方法和 GET 方法有那些區別?
1. GET用于向服務器請求數據,POST用于提交數據
2. GET請求,請求參數拼接形式暴露在地址欄,而POST請求參數則放在請求體里面,因此GET請求不適合用于驗證密碼等操作
3. GET請求的URL有長度限制,POST請求不會有長度限制
-
請簡單的介紹下APNS發送系統消息的機制
APNS優勢:杜絕了類似安卓那種為了接受通知不停在后臺喚醒程序保持長連接的行為,由iOS系統和APNS進行長連接替代。
APNS的原理:
1). 應用在通知中心注冊,由iOS系統向APNS請求返回設備令牌(device Token)
2). 應用程序接收到設備令牌并發送給自己的后臺服務器
3). 服務器把要推送的內容和設備發送給APNS
4). APNS根據設備令牌找到設備,再由iOS根據APPID把推送內容展示
聯系
github地址:https://github.com/meetly
希望大家多多指教!