亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

一道值得深入思考的iOS面試題詳解

2019-10-21 18:22:00
字體:
來源:轉載
供稿:網友

前言

最近在群里看到有人發的一道面試題,題目如下:

@interface Spark : NSObject @property(nonatomic,copy) NSString *name; @end@implementation Spark- (void)speak { NSLog(@"My name is:%@",self.name); }@end@implementation ViewController- (void)viewDidLoad { [super viewDidLoad];  id cls = [Spark class];  void *obj = &cls;  [(__bridge id)obj speak];}

問題:上述代碼運行起來會:Complie error?|Runtime crash?|NSLog ?

最終問題就是這段代碼的運行結果。

過程

第一眼看這個問題,我直接就想說,這個東西啊,肯定是編譯報錯了、要不就是崩潰啊

所以我就跟著寫了些代碼,結果發現:

WTF? 怎么能運行,而且結果竟然還是

iOS,面試題

相信當你看到這個結果的時候會和我一樣吃驚,不和邏輯啊,怎么竟然能執行成功并且還打印出來當前controller了,不符合常理啊。

解析

對于計算機而言,不存在什么魔法,如果一段代碼能運行必然存在它的原理。

我們需要做的就是分析為什么能成功。

為什么調用不崩潰

我們需要了解,cls的意思。

cls在C語言里,就是一個指針,這個指針的內容指向Spark類

當我們通過void *obj = &cls;這個語句執行后,獲取的就是一個指向這個指針cls的指針

事實上在這一步操作實現后,obj 這個指針就已經具有Object-c對象的功能了,為什么呢?接下來我們可以看看runtime實現原理了,這里我只說一點

//對象struct objc_object { Class isa OBJC_ISA_AVAILABILITY;};//類 struct objc_class { Class isa OBJC_ISA_AVAILABILITY;#if !__OBJC2__ Class super_class     OBJC2_UNAVAILABLE; const char *name      OBJC2_UNAVAILABLE; long version      OBJC2_UNAVAILABLE; long info      OBJC2_UNAVAILABLE; long instance_size     OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars    OBJC2_UNAVAILABLE; struct objc_method_list **methodLists   OBJC2_UNAVAILABLE; struct objc_cache *cache     OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols   OBJC2_UNAVAILABLE;#endif} OBJC2_UNAVAILABLE;//方法列表struct objc_method_list { struct objc_method_list *obsolete   OBJC2_UNAVAILABLE; int method_count      OBJC2_UNAVAILABLE;#ifdef __LP64__ int space      OBJC2_UNAVAILABLE;#endif /* variable length structure */ struct objc_method method_list[1]   OBJC2_UNAVAILABLE;}        OBJC2_UNAVAILABLE;//方法struct objc_method { SEL method_name      OBJC2_UNAVAILABLE; char *method_types     OBJC2_UNAVAILABLE; IMP method_imp      OBJC2_UNAVAILABLE;}

 

上述簡介中部分是錯誤的,因為這個只是在<objc/runtime.h>中的顯示,但是覺得直接刪除又顯現不出更改。因而在此專門寫出,我會在下面給出正確的解釋與數據來源

struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache;  // formerly cache pointer and vtable class_data_bits_t bits;}

數據來源: 蘋果obj4開源代碼 第1012行 用以替換 上述簡述引用中的 objc_class

可以看到objc_object這個對象的首字段是isa 指向一個Class

也就是說,我們如果有一個指向Class的地址的指針,相當于這個對象就已經可以使用了,只是像他的成員變量等等的一系列值都還沒有被初始化。

所以接下來用(__bridge id)obj,調用是不會產生問題的

為什么能打印出ViewController對象?

這個問題就是由兩個小部分組成的

1.  name 這個屬性是什么時候賦的值?

2.  ViewController 這個對象是什么時候被傳入的?

首先我們需要先了解一下,一個類對象的數據是如何存儲的。

這里我就按照上文一樣引用很多的論證了,我們自己來探究

該上代碼了:

@interface Cls : NSObject @property(nonatomic,strong) NSString *test; @property(nonatomic,strong) NSString *test1;@end@implementation Cls- (void)printPrinter { NSLog(@"self:%p",self); NSLog(@"self.test:%p",&_test); NSLog(@"self.test1:%p",&_test1);}@end

接下來調用printPrinter,打印一下對象指針地址:

iOS,面試題

可以發現,指針偏移量成員變量和指針首地址差8個字節,每個成員變量與上一個成員變量偏移量也是8個字節。

完成到這一步,我們仍然沒有發現上述兩個問題是應該怎么解釋。但是我們知道了,一個Object-C 對象的指針,和它的成員變量的指針肯定是連續的。這就為接下來我們的分析提供了一些思路。

下一步,我在原本的題目中增加一行代碼:

[super viewDidLoad];NSString *str = @"11111"; id cls = [Spark class];

為啥要增加這行代碼呢,這步是經過深(瞎)思(J)熟(B)慮(試),主要是考慮到函數內部的參數生成必然會需要地方存儲,但這部分存儲地址,我們是不知曉的,它的實現是被系統隱藏的。而我們的代碼又沒有明顯的設置相關代碼,那么必然是由這些條件實現的。所以當我們增加了這一行代碼后,不出意外的,打印結果變了

2018-11-29 20:49:39.254021+0800 test[1961:92498] My name is:11111

變成了 我們 上述的值,這一切都和猜想的差不多

于是一個基本設想就出來了:

因為棧上的地址結構和原本類的需求地址結構高度重合了,同時所有地址都能訪問到對應的值。我們通過棧的默認行為生成了一個Spark對象!

為了驗證,我們打印一下cls和str的指針堆棧地址

NSLog(@"cls address:%p str address:%p",&cls,&str);

2018-11-29 21:03:30.490989+0800 test[2129:122769] cls address:0x7ffeebf4fa00 str address:0x7ffeebf4fa08

我們可以看到他們之間相差也正好是8,而且正好和對象結構體定義的一模一樣。所以這也正好能說明我們上述的打印結果My name is:11111為什么會發生。

注:這個存在的原因是因為函數內部變量采用的小端模式,也就是將參數地址由棧區從高地址依次向低地址分配,所以我們打印cls地址會比str要小。

由此,第一個小問題就解決了,答案是因為我們在生成堆棧參數的時候,拼湊出了Spark對象的地址數據結構格式,和真正的對象地址數據結構一樣,所以self.name就是在生成cls的那一刻起內存地址就已經被賦值了。

接下來到下一個問題了ViewController 是什么時候傳入的?

在這一步里我們只能把目光向cls對象生成前執行的操作來看,[super viewDidLoad];我們只執行了這一步操作,那必然是這個操作產生的結果。為了驗證,我們可以更改一下調用順序

id cls = [Cls class]; [super viewDidLoad];

當我們進行這部操作后,會發現,執行speak方法時崩潰了,錯誤是EXC_BAC_ACCESS,說明是我們引用野指針了。
由此也可以證實,[super viewDidLoad];肯定做了一些騷操作,將ViewController的self壓入了棧區。

接下來我們就需要探究究竟做了什么操作,我們可以用如下的命令行代碼將ViewController.m重寫成c++代碼,然后觀看發生了什么。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o ViewController.cpp
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) { ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));

我們可以發現原本這個方法里面會傳入兩個參數一個是self,一個是_cmd,當我們調用[super viewDidLoad]時,執行的方法中傳入了參數self,由此將self做為一個值壓入了棧中,但是_cmd這個參數并未被使用,因此,沒有被壓入棧中。

至此,這個問題已經被解釋出來了。

答案

所有NSObject對象的首地址都是指向這個對象的所屬類。這個條件是充要條件。反過來說,如果一個地址指向某個類,我們就可以把這個地址當成對象去用。所以編譯是會通過的,也不會報unrecognized selector的錯誤。

打印結果會是ViewController對象的原因是因為cls在棧上的數據結構符合了它作為真實的類時候的數據結構,cls.name原本地址正好是棧上ViewController對象地址,因此NSLog能打印出<ViewController >

思索

這類問題,考察的東西很深,并且結合了很多知識點。但是當我們拿到面試題并且能進行思索的時候一定要好好的考慮,我對這道題的想法,也是在不斷的試驗中逐漸的完善,并且嘗試了很多。其實找面試題為什么是這個答案的過程和,找代碼找bug的流程都是類似的,都是排除變量,逐步探索,最終將探索過程和概念結合。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


注:相關教程知識閱讀請移步到IOS開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲最大福利视频网| 欧美精品久久久久久久久| 色久欧美在线视频观看| 欲色天天网综合久久| 黑人巨大精品欧美一区免费视频| 欧美乱大交做爰xxxⅹ性3| 青青草国产精品一区二区| 茄子视频成人在线| 国产一区二区三区在线观看网站| 久久久国产精品x99av| 色综合久久88色综合天天看泰| 成人亚洲欧美一区二区三区| 欧美三级免费观看| 欧美一级淫片videoshd| 久久国内精品一国内精品| 欧美激情国产高清| 亚洲成人网在线观看| 91久久久久久久久久久久久| 国产日韩欧美黄色| 久久人人爽人人爽爽久久| 亚洲尤物视频网| 欧美视频免费在线观看| 精品国产乱码久久久久久天美| 一区三区二区视频| 国产精品观看在线亚洲人成网| 国产午夜精品免费一区二区三区| 精品成人69xx.xyz| 国产精品第七影院| 九九热最新视频//这里只有精品| 国模吧一区二区三区| 成人激情av在线| 亚洲精品国精品久久99热一| 亚洲精品天天看| 91精品久久久久久久久青青| 国产精品夜色7777狼人| 国产欧美韩国高清| 欧美激情一区二区三区在线视频观看| 精品国产一区二区三区四区在线观看| 成人免费视频网址| 国产精品99蜜臀久久不卡二区| 欧美午夜美女看片| 欧美久久久精品| 92国产精品久久久久首页| 久久偷看各类女兵18女厕嘘嘘| 欧美性视频在线| 国产精品h在线观看| 成人观看高清在线观看免费| 久久人人97超碰精品888| 亚洲欧美综合区自拍另类| 欧美成人三级视频网站| 国产在线观看精品| 国产精品一区专区欧美日韩| 亚洲欧美成人在线| 日韩精品极品毛片系列视频| 日本91av在线播放| 国产精品精品久久久久久| 国产精品中文字幕在线观看| 日韩欧美成人免费视频| 国产综合香蕉五月婷在线| 992tv在线成人免费观看| 国产成人精品午夜| 精品久久久久久亚洲国产300| 色99之美女主播在线视频| 97人洗澡人人免费公开视频碰碰碰| 久久国产精品视频| 欧美激情在线狂野欧美精品| 国产精品久久电影观看| 欧美日韩视频免费播放| 热久久99这里有精品| 日韩欧美有码在线| 亚洲精品久久久一区二区三区| 最好看的2019的中文字幕视频| 日本精品久久久久久久| 亚洲精品视频二区| 日韩av电影手机在线观看| 伊人久久大香线蕉av一区二区| 另类专区欧美制服同性| 亚洲免费精彩视频| 精品美女国产在线| 欧美日韩亚洲一区二区三区| 国产精品天天狠天天看| 91精品美女在线| 国产丝袜一区二区| 免费91麻豆精品国产自产在线观看| 国产成人精品电影| 国产成人精品最新| 2020久久国产精品| 成人xxxxx| 欧美性感美女h网站在线观看免费| 国产在线观看91精品一区| 久久久久亚洲精品成人网小说| 久久综合色88| 亚洲www在线| 日韩成人在线电影网| 亚洲精品自拍偷拍| 国产不卡av在线| 欧美性一区二区三区| 伊人一区二区三区久久精品| 韩日欧美一区二区| 欧美精品videofree1080p| 亚洲成色777777在线观看影院| 久久不射电影网| 亚洲一区二区日本| 久久免费视频在线观看| 亚洲国产91精品在线观看| 欧美高清视频在线播放| 久久久久久久亚洲精品| 亚洲日韩中文字幕| 欧美怡红院视频一区二区三区| 欧美福利视频在线| 亚洲国产精品va在线看黑人动漫| 91精品国产乱码久久久久久蜜臀| 国产亚洲欧美日韩美女| 按摩亚洲人久久| 伊人一区二区三区久久精品| 欧美综合第一页| 中文字幕成人精品久久不卡| 久久亚洲精品中文字幕冲田杏梨| 欧美视频在线免费| 国产精品成人在线| 亚洲男女性事视频| 97精品国产97久久久久久春色| 日韩精品亚洲元码| 亚洲精品日产aⅴ| 欧美专区国产专区| 成人免费看黄网站| 国内精品一区二区三区| 亚洲国产精品专区久久| 91免费视频网站| 韩国v欧美v日本v亚洲| 性欧美激情精品| 欧美一级在线播放| 亚洲国产欧美日韩精品| yw.139尤物在线精品视频| 国产精品一区久久| 在线激情影院一区| 一区二区三区 在线观看视| 欧美黄色小视频| 欧美精品一区三区| 亚洲欧美激情一区| 成人黄色在线播放| 清纯唯美日韩制服另类| 欧美丰满少妇xxxx| 日韩在线资源网| 亚洲国产精品va在线观看黑人| 亚洲电影免费观看高清完整版在线观看| 亚洲成人激情视频| 国内精品小视频在线观看| 欧美精品videofree1080p| 欧美成人免费大片| 亚洲bt天天射| 91精品久久久久久久久| 91精品国产自产91精品| 欧美午夜电影在线| 国产视频精品va久久久久久| 国产精品久久久久9999| 国产精品综合不卡av| 国产精品久久久久久超碰| 亚洲人成电影网站色| 国产精品久久久久7777婷婷| 日韩av成人在线| 国产成人短视频| 欧美激情亚洲自拍| 亚洲欧美日韩天堂一区二区|