lldb(gdb)常用的調試命令?
- po:打印對象,會調用對象description方法。是print-object的簡寫
- expr:可以在調試時動態(tài)執(zhí)行指定表達式,并將結果打印出來,很有用的命令
- print:也是打印命令,需要指定類型
- bt:打印調用堆棧,是thread backtrace的簡寫,加all可打印所有thread的堆棧
- br l:是breakpoint list的簡寫
BAD_ACCESS在什么情況下出現?
- 訪問一個僵尸對象,訪問僵尸對象的成員變量或者向其發(fā)消息
- 死循環(huán)
如何調試BAD_ACCESS錯誤
- 設置全局斷點快速定位問題代碼所在行
? 開啟僵尸對象調試功能
簡述下Objective-C中調用方法的過程(runtime)
Objective-C是動態(tài)語言,每個方法在運行時會被動態(tài)轉為消息發(fā)送,即:objc_msgSend(receiver, selector),整個過程介紹如下:
objc在向一個對象發(fā)送消息時,runtime庫會根據對象的isa指針找到該對象實際所屬的類
然后在該類中的方法列表以及其父類方法列表中尋找方法運行
如果,在最頂層的父類(一般也就NSObject)中依然找不到相應的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX
但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會,這三次拯救程序奔潰的說明見問題《什么時候會報unrecognized selector的異常》中的說明
補充說明:Runtime 鑄就了Objective-C 是動態(tài)語言的特性,使得C語言具備了面向對象的特性,在程序運行期創(chuàng)建,檢查,修改類、對象及其對應的方法,這些操作都可以使用runtime中的對應方法實現。
什么是method swizzling(俗稱黑魔法)
- 簡單說就是進行方法交換
- 在Objective-C中調用一個方法,其實是向一個對象發(fā)送消息,查找消息的唯一依據是selector的名字。利用Objective-C的動態(tài)特性,可以實現在運行時偷換selector對應的方法實現,達到給方法掛鉤的目的
? 每個類都有一個方法列表,存放著方法的名字和方法實現的映射關系,selector的本質其實就是方法名,IMP有點類似函數指針,指向具體的Method實現,通過selector就可以找到對應的IMP
* 交換方法的幾種實現方式
- 利用 method_exchangeImplementations 交換兩個方法的實現
- 利用 class_replaceMethod 替換方法的實現
? 利用 method_setImplementation 來直接設置某個方法的IMP
objc中向一個nil對象發(fā)送消息將會發(fā)生什么?
在Objective-C中向nil發(fā)送消息是完全有效的——只是在運行時不會有任何作用
如果一個方法返回值是一個對象,那么發(fā)送給nil的消息將返回0(nil)
如果方法返回值為指針類型,其指針大小為小于或者等于sizeof(void*)
float,double,long double 或者long long的整型標量,發(fā)送給nil的消息將返回0
如果方法返回值為結構體,發(fā)送給nil的消息將返回0。結構體中各個字段的值將都是0
如果方法的返回值不是上述提到的幾種情況,那么發(fā)送給nil的消息的返回值將是未定義的
具體原因分析
objc是動態(tài)語言,每個方法在運行時會被動態(tài)轉為消息發(fā)送,即:objc_msgSend(receiver, selector)
為了方便理解這個內容,還是貼一個objc的源代碼
struct objc_class
* {
* // isa指針指向Meta Class,因為Objc的類的本身也是一個Object,
* // 為了處理這個關系,runtime就創(chuàng)造了Meta Class,
* // 當給類發(fā)送[NSObject alloc]這樣消息時,實際上是把這個消息發(fā)給了Class Object
* Class isa OBJC_ISA_AVAILABILITY;
* #if !__OBJC2__
* Class super_class OBJC2_UNAVAILABLE; // 父類
* const char *name OBJC2_UNAVAILABLE; // 類名
* long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
* long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標識
* long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
* struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
* struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
* // 方法緩存,對象接到一個消息會根據isa指針查找消息對象,
* // 這時會在method Lists中遍歷,
* // 如果cache了,常用的方法調用時就能夠提高調用的效率。
* // 這個方法緩存只存在一份,不是每個類的實例對象都有一個方法緩存
* // 子類會在自己的方法緩存中緩存父類的方法,父類在自己的方法緩存中也會緩存自己的方法,而不是說子類就不緩存父類方法了
* struct objc_cache *cache OBJC2_UNAVAILABLE;
* struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協議鏈表
* #endif
* } OBJC2_UNAVAILABLE;
- objc在向一個對象發(fā)送消息時,runtime庫會根據對象的isa指針找到該對象實際所屬的類,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,然后再發(fā)送消息的時候,objc_msgSend方法不會返回值,所謂的返回內容都是具體調用時執(zhí)行的。
- 如果向一個nil對象發(fā)送消息,首先在尋找對象的isa指針時就是0地址返回了,所以不會出現任何錯誤
objc中向一個對象發(fā)送消息[obj foo]和objc_msgSend()函數之間有什么關系?
- [obj foo];在objc動態(tài)編譯時,會被轉意為:objc_msgSend(obj, @selector(foo));
什么時候會報unrecognized selector的異常?
當調用該對象上某個方法,而該對象上沒有實現這個方法的時候, 可以通過“消息轉發(fā)”進行解決,如果還是不行就會報unrecognized selector異常
objc是動態(tài)語言,每個方法在運行時會被動態(tài)轉為消息發(fā)送,即:objc_msgSend(receiver, selector),整個過程介紹如下:
objc在向一個對象發(fā)送消息時,runtime庫會根據對象的isa指針找到該對象實際所屬的類
然后在該類中的方法列表以及其父類方法列表中尋找方法運行
如果,在最頂層的父類中依然找不到相應的方法時,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前,objc的運行時會給出三次拯救程序崩潰的機會
三次拯救程序崩潰的機會
Method resolution
objc運行時會調用+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機會提供一個函數實現。
如果你添加了函數并返回 YES,那運行時系統就會重新啟動一次消息發(fā)送的過程
如果 resolve 方法返回 NO ,運行時就會移到下一步,消息轉發(fā)
Fast forwarding
如果目標對象實現了-forwardingTargetForSelector:,Runtime 這時就會調用這個方法,給你把這個消息轉發(fā)給其他對象的機會
只要這個方法返回的不是nil和self,整個消息發(fā)送的過程就會被重啟,當然發(fā)送的對象會變成你返回的那個對象。
否則,就會繼續(xù)Normal Fowarding。
這里叫Fast,只是為了區(qū)別下一步的轉發(fā)機制。因為這一步不會創(chuàng)建任何新的對象,但Normal forwarding轉發(fā)會創(chuàng)建一個NSInvocation對象,相對Normal forwarding轉發(fā)更快點,所以這里叫Fast forwarding
Normal forwarding
這一步是Runtime最后一次給你挽救的機會。
首先它會發(fā)送-methodSignatureForSelector:消息獲得函數的參數和返回值類型。
如果-methodSignatureForSelector:返回nil,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。
如果返回了一個函數簽名,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標對象
HTTP協議中POST方法和GET方法有那些區(qū)別?
- GET用于向服務器請求數據,POST用于提交數據
- GET請求,請求參數拼接形式暴露在地址欄,而POST請求參數則放在請求體里面,因此GET請求不適合用于驗證密碼等操作
- GET請求的URL有長度限制,POST請求不會有長度限制
使用block時什么情況會發(fā)生引用循環(huán),如何解決?
在block內如何修改block外部變量?
使用系統的某些block api(如UIView的block版本寫動畫時),是否也考慮循環(huán)引用問題?
- 系統的某些block api中,UIView的block版本寫動畫時不需要考慮,但也有一些api 需要考慮
- 以下這些使用方式不會引起循環(huán)引用的問題
[UIView animateWithDuration:duration animations:^
{ [self.superview layoutIfNeeded]; }];
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{ self.someProperty = xyz; }];
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification * notification)
{ self.someProperty = xyz; }];
但如果方法中的一些參數是 成員變量,那么可以造成循環(huán)引用,如 GCD 、NSNotificationCenter調用就要小心一點,比如 GCD 內部如果引用了 self,而且 GCD 的參數是 成員變量,則要考慮到循環(huán)引用,舉例如下:
GCD
分析:self-->_operationsQueue-->block-->self形成閉環(huán),就造成了循環(huán)引用 __weak typeof(self) weakSelf = self;
* dispatch_group_async(_operationsGroup, _operationsQueue, ^
* {
* [weakSelf doSomething];
* [weakSelf doSomethingElse];
* } );
NSNotificationCenter
分析:self-->_observer-->block-->self形成閉環(huán),就造成了循環(huán)引用
* __weak __typeof__(self) weakSelf = self;
* _observer = [[NSNotificationCenter defaultCenter]
* addObserverForName:@"testKey"
* object:nil
* queue:nil
* usingBlock:^(NSNotification *note){
* [weakSelf dismissModalViewControllerAnimated:YES];
* }];
OC中常見的循環(huán)引用總結