1摊趾、系統(tǒng)UI事件的傳遞機制?
事件傳遞:當(dāng)在視圖上觸發(fā)一個事件時游两,系統(tǒng)首先會判斷主窗口(keyWindow)是否能夠接受觸摸事件砾层,當(dāng)無法接受處理時,會在子視圖中進行逐級查找判斷贱案,直到遍歷查找出合適的處理視圖
事件響應(yīng):當(dāng)事件傳遞之后肛炮,開始進行事件響應(yīng),事件響應(yīng)是沿著事件傳遞鏈條反向響應(yīng)宝踪,即從子視圖-->父視圖(上級響應(yīng)鏈)的過程侨糟;如果所有視圖都無法響應(yīng)該觸摸事件,則系統(tǒng)會直接拋棄該觸摸事件
涉及方法:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
只要事件一傳遞給一個控件,這個控件就會調(diào)用
注:【不管這個控件能不能處理事件瘩燥,也不管觸摸點在不在這個控件上粟害,
事件都會先傳遞給這個控件,隨后再調(diào)用hitTest:withEvent:方法】
當(dāng) return nil 時颤芬,則表明攔截該視圖及子視圖的所有事件
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
用來判斷觸發(fā)點是否在該視圖內(nèi)部
事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞是從上到下(父控件到子控件)
事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件)
2悲幅、消息傳遞機制與消息轉(zhuǎn)發(fā)流程套鹅?
消息傳遞:
1、接收者根據(jù)isa指針查找所屬類的“方法列表”(method list)中從上向下遍歷汰具。如果能找到與選擇子名稱相符的方法卓鹿,就根據(jù)IMP指針(IMP:函數(shù)指針,保存了方法地址留荔。)跳轉(zhuǎn)到方法的實現(xiàn)代碼吟孙,調(diào)用這個方法的實現(xiàn)。
2聚蝶、如果在所屬類中未找到相符方法杰妓,則繼續(xù)在集成體系中網(wǎng)上查找,然后同上
3碘勉、如果在集成體系中都未找到相符方法巷挥,就會執(zhí)行 消息轉(zhuǎn)發(fā) 操作
消息轉(zhuǎn)發(fā):
一個完整的消息轉(zhuǎn)發(fā)過程會經(jīng)歷三個階段:
動態(tài)方法解析(resolveInstanceMethod或resolveClassMethod):【本類有機會新增一個處理選擇子的方法】
備選接收者(forwardingTargetForSelecor)
完整消息轉(zhuǎn)發(fā)(forwardInvocation)
當(dāng)消息轉(zhuǎn)發(fā)之后,依然沒有響應(yīng)該方法時验靡,即會報錯無法識別方法的錯誤
涉及方法:
void objc_msgSend(id self, SEL cmd, ...)
例如:
id returnValue = objc_msgSend(someObject, @selector(messageName:), parameter)
這是一個參數(shù)個數(shù)可變的函數(shù)倍宾。能接收兩個或兩個以上的參數(shù),
第一個參數(shù)代表接受者(調(diào)用者)胜嗓,
第二個參數(shù)代表選擇子(方法)高职。
后續(xù)參數(shù)就是消息中的那些參數(shù),其順序不變辞州。
接受者就是調(diào)用方法的對象或者類(本質(zhì)上類也是對象怔锌,叫做類對象)。
動態(tài)方法解析
+ (BOOL)resolveInstanceMethod : (SEL)selector
+ (BOOL)resolveClassMethod : (SEL)selector
備選接收者
- (id)forwardingTargetForSelector:(SEL)selector
完整消息轉(zhuǎn)發(fā)
- (void)forwardInvocation:(NSInvocation *)invocation
3变过、異步事件如何控制執(zhí)行順序埃元?
1、 runLoop
-(void)runLoopTest{
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSLog(@"A");
//RunLoop停止 并獲取主線程
CFRunLoopStop(CFRunLoopGetMain());
}] ;
[task resume];
CFRunLoopRun();
NSLog(@"B");
}
2牵啦、 dispatch_group_notify
-(void)gcdGroupTest{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
NSLog(@"A");
});
//通知執(zhí)行完畢
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"B");
});
3亚情、 barrier 阻塞
-(void)dispath_barrier_asyncTest{
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"A");
});
//barrier 阻塞
dispatch_barrier_async(queue, ^{
NSLog(@"拿到了A的值");
});
dispatch_async(queue, ^{
NSLog(@"B");
});
}
4、NSOperation dependency 操作隊列添加依賴
-(void)operationDependencyTest{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *p1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"A");
}];
NSBlockOperation *p2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"B");
}];
NSBlockOperation *p3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"C");
}];
//p2 依賴p1
//p3 依賴p2
[p2 addDependency:p1];
[p3 addDependency:p2];
// waitUntilFinished是否阻塞當(dāng)前線程
[queue addOperations:@[p1,p2,p3] waitUntilFinished:NO];
}
4哈雏、__weak和__block修飾符的使用
__block對象在block中是可以被修改楞件、重新賦值的,因為__block 將對象的內(nèi)存地址指針放到了堆中裳瘪,是的對象不管是在block的內(nèi)部還是外部土浸,都能夠保證內(nèi)存地址相同,進而可以被修改
__weak修飾符的對象彭羹,作用等同于定義為weak的property黄伊。自然不會導(dǎo)致循環(huán)引用問題,因為蘋果文檔已經(jīng)說的很清楚派殷,當(dāng)原對象沒有任何強引用的時候还最,弱引用指針也會被設(shè)置為nil
__block和__weak修飾符的區(qū)別其實是挺明顯的:
1.__block不管是ARC還是MRC模式下都可以使用墓阀,可以修飾對象,還可以修飾基本數(shù)據(jù)類型拓轻。
2.__weak只能在ARC模式下使用斯撮,也只能修飾對象(NSString),不能修飾基本數(shù)據(jù)類型(int)扶叉。
3.__block對象可以在block中被重新賦值勿锅,__weak不可以。
4.__block對象在ARC下可能會導(dǎo)致循環(huán)引用枣氧,非ARC下會避免循環(huán)引用溢十,__weak只在ARC下使用,可以避免循環(huán)引用
5达吞、枚舉中 NS_ENUM 和 NS_OPTIONS 宏來定義的區(qū)別张弛?
官方推薦第一種寫法
typedef NS_OPTIONS(NSUInteger, UISwipeGestureRecognizerDirection) {
UISwipeGestureRecognizerDirectionNone = 0, //值為0
UISwipeGestureRecognizerDirectionRight = 1 << 0, //值為2的0次方
UISwipeGestureRecognizerDirectionLeft = 1 << 1, //值為2的1次方
UISwipeGestureRecognizerDirectionUp = 1 << 2, //值為2的2次方
UISwipeGestureRecognizerDirectionDown = 1 << 3 //值為2的3次方
};
typedef enum {
UISwipeGestureRecognizerDirectionNone = 0, //值為0
UISwipeGestureRecognizerDirectionRight = 1 << 0, //值為2的0次方
UISwipeGestureRecognizerDirectionLeft = 1 << 1, //值為2的1次方
UISwipeGestureRecognizerDirectionUp = 1 << 2, //值為2的2次方
UISwipeGestureRecognizerDirectionDown = 1 << 3 //值為2的3次方
}UISwipeGestureRecognizerDirection;
官方推薦第一種寫法
typedef NS_ENUM(NSInteger, NSWritingDirection) {
NSWritingDirectionNatural = 0, //值為0
NSWritingDirectionLeftToRight = 2, //值為2
NSWritingDirectionRightToLeft //值為3
};
typedef enum {
NSWritingDirectionNatural = -1, //值為-1
NSWritingDirectionLeftToRight = 0, //值為0
NSWritingDirectionRightToLeft = 1 //值為1
}NSWritingDirection;
NS_ENUM為通用枚舉,枚舉項的值為 NSInteger宗挥,若是定義了枚舉項其中一項的值后面依次在它的前一項的值上加1
NS_OPTIONS為位移枚舉乌庶,枚舉項的值為NSUInteger种蝶,枚舉項的值需要像這樣表示1 << 0契耿,1 << 1,2的幾次方這樣螃征,表示需要用到按位或(2個及以上枚舉值可多個存在)
注意:
在使用或運算操作兩個枚舉值時搪桂,
C++默認為運算結(jié)果的數(shù)據(jù)類型是枚舉的底層數(shù)據(jù)類型即NSUInteger,
且C++不允許它隱式轉(zhuǎn)換為枚舉類型本身,
所以C++模式下定義了NS_OPTIONS宏以保證不出現(xiàn)類型轉(zhuǎn)換
6盯滚、@synthesize和@dynamic分別有什么作用踢械?
1、@property有兩個對應(yīng)的詞魄藕,一個是 @synthesize内列,一個是 @dynamic。如果 @synthesize和 @dynamic都沒寫背率,那么默認的就是@syntheszie var = _var
2话瞧、@synthesize 的語義是如果你沒有手動實現(xiàn) setter 方法和 getter 方法,那么編譯器會自動為你加上這兩個方法寝姿,當(dāng)同時重寫 setter交排、getter 方法時,需要定義 @syntheszie var = _var
3饵筑、@dynamic 告訴編譯器:屬性的 setter 與 getter 方法由用戶自己實現(xiàn)埃篓,不自動生成。(當(dāng)然對于 readonly 的屬性只需提供 getter 即可)根资。假如一個屬性被聲明為 @dynamic var架专,然后你沒有提供 @setter方法和 @getter 方法同窘,編譯的時候沒問題,但是當(dāng)程序運行到 instance.var = someVar部脚,由于缺 setter 方法會導(dǎo)致程序崩潰塞椎;或者當(dāng)運行到 someVar = var 時,由于缺 getter 方法同樣會導(dǎo)致崩潰睛低。編譯時沒問題案狠,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定钱雷。
7骂铁、copy與mutableCopy的區(qū)別?
1. 對非集合類對象的copy操作:
[immutableObject copy] // 淺復(fù)制(指針拷貝)
[immutableObject mutableCopy] //深復(fù)制(對象拷貝)
[mutableObject copy] //深復(fù)制
[mutableObject mutableCopy] //深復(fù)制
在非集合類對象中:
對 immutable 對象進行 copy 操作罩抗,是指針復(fù)制拉庵,mutableCopy 操作時內(nèi)容復(fù)制;
對 mutable 對象進行 copy 和 mutableCopy 都是內(nèi)容復(fù)制
2套蒂、集合類對象的copy與mutableCopy
[immutableObject copy] // 淺復(fù)制
[immutableObject mutableCopy] //單層深復(fù)制
[mutableObject copy] //單層深復(fù)制
[mutableObject mutableCopy] //單層深復(fù)制
在集合類對象中:
對 immutable 對象進行 copy钞支,是指針復(fù)制, mutableCopy 是內(nèi)容復(fù)制操刀;
對 mutable 對象進行 copy 和 mutableCopy 都是內(nèi)容復(fù)制烁挟。
但是:集合對象的內(nèi)容復(fù)制僅限于對象本身,對象元素仍然是指針復(fù)制
8骨坑、字符串轉(zhuǎn)數(shù)值實現(xiàn)方案:
在將一個字符串轉(zhuǎn)成數(shù)值類型之前撼嗓,需要先對該字符串進行校驗,看是否為數(shù)值類型字符串欢唾,否則無法進行轉(zhuǎn)換且警。下面列出的為兩種校驗方案:
方案一:利用謂詞校驗
/// 使用正則判斷是否為數(shù)值字符串
/// @param str 原始字符串
- (BOOL)judgeIsNumberStrWithPredicate:(NSString *)str {
if (str.length == 0) {
return NO;
}
NSArray * arr = [str componentsSeparatedByString:@"."];
if (arr.count > 2) { ///< 表示字符串中有多個小數(shù)點(.)不滿足
return NO;
}
str = [str stringByReplacingOccurrencesOfString:@"." withString:@""];
NSString * regex = @"[0-9]*|-[0-9]";
NSPredicate * pred = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
if ([pred evaluateWithObject:str]) {
return YES;
}
return NO;
}
方案二:利用系統(tǒng) NSCharacterSet 類進行校驗
/// 使用 NSCharacterSet API 判斷是否為數(shù)值字符串
/// @param str 原始字符串
- (BOOL)judgeIsNumberStrWithCharacter:(NSString *)str {
str = [str stringByTrimmingCharactersInSet:[NSCharacterSet decimalDigitCharacterSet]];
if (str.length > 0 && ![str isEqualToString:@"."]) { ///< 截取數(shù)字之后,還有其他非 “.” 的字符礁遣,不滿足
return NO;
}
return YES;
}
轉(zhuǎn)換字符串為數(shù)值類型調(diào)用
- (void)viewDidLoad {
[super viewDidLoad];
NSNumber * n1 = [self numberFromStr:@"100.9898"];
NSNumber * n2 = [self numberFromStr:@"huda01192"];
NSNumber * n3 = [self numberFromStr:@"0393721"];
NSNumber * n4 = [self numberFromStr:@"98372.198.98"];
NSLog(@"\n-------n1 = %@斑芜,\n-------n2 = %@,\n-------n3 = %@祟霍,\n-------n4 = %@杏头,\n",n1,n2,n3,n4);
///輸出:-------n1 = 100.9898,-------n2 = (null)浅碾,-------n3 = 393721大州,-------n4 = (null),
}
- (NSNumber *)numberFromStr:(NSString *)str {
/// 檢驗數(shù)值字符串垂谢,可替換成 [self judgeIsNumberStrWithCharacter:str]
if ([self judgeIsNumberStrWithPredicate:str]) {
NSNumber * num = @([[NSDecimalNumber decimalNumberWithString:str] floatValue]);
return num;
}
return nil;
}
打印輸出結(jié)果為:
9厦画、動態(tài)庫加載過程
參考:
http://www.reibang.com/p/8aae2861204a
http://www.reibang.com/p/72e34948dac0
http://www.reibang.com/p/5f337da8fbef
在+(void)load
中進行斷點調(diào)試發(fā)現(xiàn),運行期間首先系統(tǒng)會調(diào)用dyld
中的dyldbootstrap
文件的start
方法。最后start函數(shù)執(zhí)行了一個main函數(shù)(這個可以不是我們app中的main函數(shù)根暑,而是dyld的)并返回力试,所以APP的啟動流程為:
1、設(shè)置運行環(huán)境排嫌,配置環(huán)境變量畸裳;(設(shè)置了全局一個鏈接上下文,包括一些回調(diào)函數(shù)淳地、參數(shù)與標(biāo)志設(shè)置信息怖糊,還有線程相關(guān)的一些配置)
2、加載共享緩存庫颇象;(所有默認的動態(tài)鏈接庫被合并成一個大的緩存文件伍伤,放到/System/Library/Caches/com.apple.dyld/目錄下)
3、實例化主程序遣钳;(解析MachO`(可執(zhí)行文件)`文件進行映射扰魂,根據(jù)映射信息初始化創(chuàng)建鏡像并加入到緩存中的鏡像列表)
4、加載動態(tài)鏈接庫蕴茴;
5劝评、鏈接主程序;(鏈接所有動態(tài)庫倦淀,進行符號修正綁定工作)
6蒋畜、初始化主程序 `OC, C++`全局變量初始化(`objc_init`調(diào)用`_dyld_objc_notify_register `函數(shù))
7、尋找APP的main函數(shù)并調(diào)用
10晃听、啟動時間處理
1百侧、pre-main階段優(yōu)化
1砰识、減少ObjC的類膨脹問題能扒,清理沒有使用的類,合并松散無用的類辫狼;
2初斑、減少靜態(tài)變量的聲明和初始化的分離;
3膨处、移除沒有使用的動態(tài)庫依賴见秤,明確所依賴的frameworks是require還是optional,optional會動態(tài)進行額外檢查真椿;
4鹃答、刪除沒有用的方法;
5突硝、減少+load函數(shù)的實現(xiàn)测摔,并減少在其中操作的邏輯;
2、main階段優(yōu)化
1锋八、didFinishLaunchingWithOptions減少不必要的注冊方法浙于,或?qū)⒆哉{(diào)用延后處理;
2挟纱、壓縮或減少啟動圖片羞酗;
3、盡量避免或減少阻塞代碼的執(zhí)行紊服,可以利用多線程優(yōu)化
11檀轨、KVO崩潰的情況
1、注冊監(jiān)聽欺嗤,但未實現(xiàn) - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context 方法
2裤园、多次removeObserver同一個監(jiān)聽【監(jiān)聽添加和移除次數(shù)不匹配】
12、weak原理
系統(tǒng)會創(chuàng)建一個全局的weak表(hash(哈希)表)剂府,用來存儲所有的弱引用數(shù)據(jù)信息拧揽,
其中Key是弱引用所指對象的內(nèi)存地址,Value是weak指針的內(nèi)存地址數(shù)組腺占,
即:__weak typeof(obj)weakObj = obj;
key為 &obj淤袜;
value為 &weakObj;
13衰伯、控制異步操作的執(zhí)行順序方式
例如:異步任務(wù)A铡羡、B、C意鲸,要求A在B烦周、C之后執(zhí)行
方式一:GCD串行隊列執(zhí)行
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"B");
});
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"C");
});
});
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"A");
});
});
方式二:GCD group 完成
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"B");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"C");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
dispatch_async(queue, ^{
NSLog(@"A");
});
});
方式三:GCD barrier 柵欄函數(shù)
// 自定義隊列
dispatch_queue_t que = dispatch_queue_create("frank_queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(que, ^{
NSLog(@"B");
});
dispatch_async(que, ^{
NSLog(@"C");
});
dispatch_barrier_async(que, ^{
NSLog(@"------- 隔離等待 B、C 事件完成");
});
dispatch_async(que, ^{
NSLog(@"A");
});
方式四:NSOption 添加依賴完成
NSOperationQueue * opQue = [[NSOperationQueue alloc] init];
NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"A");
}];
NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"B");
}];
NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"C");
}];
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
[opQue addOperation:operation1];
[opQue addOperation:operation2];
[opQue addOperation:operation3];