你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> 京東/優酷IOS面試題

京東/優酷IOS面試題

編輯:IOS開發綜合

京東面試題

image

1、請寫出以下代碼輸出

  int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int *)(&a + 1);
printf("%d, %d", *(a + 1), *(ptr + 1));

參考答案: 2, 隨機值

這種類型題好像挺常見的。考的就是C語言上的指針的理解和數組的理解。

分析:

a代表有5個元素的數組的首地址,a[5]的元素分別是1,2,3,4,5。接下來,a + 1表示數據首地址加1,那麼就是a[1],也就是對應於值為2.但是,這裡是&a + 1,因為a代表的是整個數組,它的空間大小為5 * sizeof(int),因此&a + 1就是a+5。a是個常量指針,指向當前數組的首地址,指針+1就是移動sizeof(int)個字節。

因此,ptr是指向int *類型的指針,而ptr指向的就是a + 5,那麼ptr + 1也相當於a + 6,所以最後的*(ptr + 1)就是一個隨機值了。而*(ptr – 1)就相當於a + 4,對應的值就是5。

2、寫一個標准宏Max,並給出以下代碼的輸出

  int array[5] = {1, 2, 3, 4, 5};
int *p = &array[0];
int max = Max(*p++, 1);
printf("%d %d", max, *p);

參考答案: 1,2

  #define Max(X, Y) ((X) > (Y) ? (X) : (Y))

當看到宏時,就會想到宏定義所帶來的副作用。對於++、–,在宏當中使用是最容易產生副作用的,因此要慎用。

分析:

p指針指向了數組array的首地址,也就是第一個元素對應的地址,其值為1.
宏定義時一定要注意每個地方要加上圓括號
*p++相當於*p, p++,所以Max(*p++, 1)相當於:

  (*p++) > (1) ? (*p++) : (1) => (1) > (1) ? (*p++) : (1) => 第一個*p++的結果是,p所指向的值變成了2,但是1 > 1為値,所以最終max的值就是1。而後面的(*p++)也就不會執行,因此p所指向的地址對應的值就是2,而不是3.

擴展:如果上面的*p++改成*(++p)如何?

分析:

  (*++p) > (1) ? (*++p) : (1) => (2) > (1) ? (*++p) : (1) => max = *++p; => *p = 3,max = 3;

3、在一個對象的方法裡:self.name=@object;和name=@object有什麼不同

參考答案:

這是老生常談的話題了,實質上就是問setter方法賦值與成員變量賦值有什麼不同。通過點語法self.name實質上就是[self setName:@object];。而name這裡是成員變量,直接賦值。

一般來說,在對象的方法裡成員變量和方法都是可以訪問的,我們通常會重寫Setter方法來執行某些額外的工作。比如說,外部傳一個模型過來,那麼我會直接重寫Setter方法,當模型傳過來時,也就是意味著數據發生了變化,那麼視圖也需要更新顯示,則在賦值新模型的同時也去刷新UI。這樣也不用再額外提供其他方法了。

4、怎樣使用performSelector傳入3個以上參數,其中一個為結構體

參考答案:

  - (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

因為系統提供的performSelector的api中,並沒有提供三個參數。因此,我們只能傳數組或者字典,但是數組或者字典只有存入對象類型,而結構體並不是對象類型,那麼怎麼辦呢?

沒有辦法,我們只能通過對象放入結構作為屬性來傳過去了:

  typedef struct HYBStruct { int a; int b; } *my_struct; @interface HYBObject : NSObject @property (nonatomic, assign) my_struct arg3; @property (nonatomic, copy)NSString *arg1; @property (nonatomic, copy) NSString *arg2; @end @implementation HYBObject // 在堆上分配的內存,我們要手動釋放掉 - (void)dealloc { free(self.arg3); } @end

測試:

  my_struct str = (my_struct)(malloc(sizeof(my_struct))); str->a = 1; str->b = 2; HYBObject *obj = [[HYBObject alloc] init]; obj.arg1 = @"arg1"; obj.arg2 = @"arg2"; obj.arg3 = str; [self performSelector:@selector(call:) withObject:obj]; // 在回調時得到正確的數據的 - (void)call:(HYBObject *)obj { NSLog(@"%d %d", obj.arg3->a, obj.arg3->b); }

5、UITableViewCell上有個UILabel,顯示NSTimer實現的秒表時間,手指滾動cell過程中,label是否刷新,為什麼?

參考答案:

這是否刷新取決於timer加入到Run Loop中的Mode是什麼。Mode主要是用來指定事件在運行循環中的優先級的,分為:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認,空閒狀態UITrackingRunLoopMode:ScrollView滑動時會切換到該ModeUIInitializationRunLoopMode:run loop啟動時,會切換到該modeNSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合

蘋果公開提供的Mode有兩個:

NSDefaultRunLoopMode(kCFRunLoopDefaultMode)NSRunLoopCommonModes(kCFRunLoopCommonModes)

如果我們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環中的時候, ScrollView滾動過程中會因為mode的切換,而導致NSTimer將不再被調度。當我們滾動的時候,也希望不調度,那就應該使用默認模式。但是,如果希望在滾動時,定時器也要回調,那就應該使用common mode。

對於這道題,如果要cell滾動過程中定時器正常回調,UI正常刷新,那麼要將timer放入到CommonModes下,因為是NSDefaultRunLoopMode,只有在空閒狀態下才會回調。

6、有a、b、c、d 4個異步請求,如何判斷a、b、c、d都完成執行?如果需要a、b、c、d順序執行,該如何實現?

參考答案:

對於這四個異步請求,要判斷都執行完成最簡單的方式就是通過GCD的group來實現: 1 2 3 4 5 6 7 8 9 10 11 12 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ /*任務a */ }); dispatch_group_async(group, queue, ^{ /*任務b */ }); dispatch_group_async(group, queue, ^{ /*任務c */ }); dispatch_group_async(group, queue, ^{ /*任務d */ }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 在a、b、c、d異步執行完成後,會回調這裡 });

當然,我們還可以使用非常老套的方法來處理,通過四個變量來標識a、b、c、d四個任務是否完成,然後在runloop中讓其等待,當完成時才退出run loop。但是這樣做會讓後面的代碼得不到執行,直到Run loop執行完畢。

要求順序執行,那麼可以將任務放到串行隊列中,自然就是按順序來異步執行了。

7、使用block有什麼好處?使用NSTimer寫出一個使用block顯示(在UILabel上)秒表的代碼。

參考答案:

說到block的好處,最直接的就是代碼緊湊,傳值、回調都很方便,省去了寫代理的很多代碼。

對於這裡根本沒有必要使用block來刷新UILabel顯示,因為都是直接賦值。當然,筆者覺得這是在考驗應聘者如何將NSTimer寫成一個通用用的Block版本。

代碼放到了這裡:NSTimer封裝成Block版

使用起來像這樣:

  NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES callback:^() { weakSelf.secondsLabel.text = ... } [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

8、一個view已經初始化完畢,view上面添加了n個button(可能使用循環創建),除用view的tag之外,還可以采用什麼辦法來找到自己想要的button來修改Button的值

參考答案:

這個問題有很多種方式,而且不同的使用場景也不一樣的。比如說:

第一種:如果是點擊某個按鈕後,才會刷新它的值,其它不用修改,那麼不用引用任何按鈕,直接在回調時,就已經將接收響應的按鈕給傳過來了,直接通過它修改即可。第二種:點擊某個按鈕後,所有與之同類型的按鈕都要修改值,那麼可以通過在創建按鈕時將按鈕存入到數組中,在需要的時候遍歷查找。

9、tableview在滑動時,有時候會大量加載本地圖片,這時候會很卡,如何解決加載耗時過長導致不流暢的問題

參考答案:

這是優化tableview的相關專題,如果只是處理圖片加載問題,那可以通過異步讀取圖片然後刷新UI。當然,我們也可以在取數據時,在模型中提前准備好需要顯示的圖片資源,這樣在cell只就不需要操作圖片讀取,而是直接顯示。

如果想要更深入地優化,學習以下知識點:

Offscreen-RenderedColor Misaligned ImagesColor Blended Layers

10、給定一個如下的字符串(1,(2,3),(4,(5,6)7))括號內的元素可以是數字,也可以是括號,請實現一個算法清除嵌套的括號,比如把上面的表達式的變成:(1,2,3,4,5,6,7),表達式有誤時請報錯。

參考答案:

如果只是判斷整個表達式是否有錯誤,然後去掉裡面的圓括號,那麼一個循環就可以了。不過我們只需要加兩個變量分別來記錄左圓括號和右圓括號的個數。這裡假設逗號總是正確的情況下,偽代碼如下:

  left = 0; rigt = 0; for i = 0; i < str.length; ++i { if 是左括號 { left++; continue; } if 是右括號 { right++; // 處理(1,)這樣的結構 if 前一個是逗號 { error; } continue; } [newStr append:str[i]]; } if left != right { error; }

感謝標哥的整理

 

 

優酷面試題

image

1、如何聲明私有變量和私有方法?

參考答案:

聲明私有變量可以通過@private關鍵字來聲明。例如,這樣就是私有的成員變量了:   @interface HYBTestModel : NSObject { @private NSString *_userName; } @end 沒有關鍵字聲明為私有方法,因為ObjC中也沒有真正意義上的私有方法。我們要讓方法成員私有,只能通過放在.m文件中定義而不暴露在外部。但是,如果有人知道內部此這麼一個方法,那麼也是可以訪問的。

先說明:ObjC中沒有絕對的私有變量和私有方法。

如何修改私有成員變量的值?

  HYBTestModel *model = [[HYBTestModel alloc] init]; // 通過KVC可以輕松修改私有成員變量 // 自己加一個打印就可以看到有值了! [model setValue:@"修改私有變量的值" forKey:@"_userName"];

那又如何訪問私有成員變量?

1 2 3 4 Ivar userNameIvar = class_getInstanceVariable([model class], "_userName"); NSString *userName = object_getIvar(model, userNameIvar);

我們可以通過runtime來獲取對象的成員變量Ivar,然後再通過object_getIvar來獲取某個對象的成員變量的值。

看到這裡,還相信ObjC中所謂私有變量嗎?

2、assign、retain、copy分別起什麼作用?重寫下面的屬性的getter/setter方法

1 2 3 @property (nonatomic, retain) NSNumber *num;

參考答案:

從題目可知這問的是MRC下的問題。在MRC下:

assign用於非對象類型,對於對象類型的只用於弱引用。retain用於對象類型,強引用對象copy用於對象類型,強引用對象。

重寫setter/getter(如何重寫getter和setter,是不會自動登錄_num成員變量的,需要自己手動聲明):

  - (NSNumber *)num { return _num; } - (void)setNum:(NSNumber *)aNum { if (_num != aNum) { [_num release]; _num = nil; _num = [aNum retain]; } }

3、如何聲明一個delegate屬性,為什麼?

參考答案:

聲明屬性時要,在ARC下使用weak,在MRC下使用assign。比如:

  @property (nonatomic, weak) id delegate;

在MRC下,使用assign是因為沒有weak關鍵字,只能使用assign來防止循環引用。在ARC下,使用weak來防止循環引用。

4、autorelease的對象何時被釋放

參考答案:

如果了解一點點Run Loop的知道,應該了解到:Run Loop在每個事件循環結束後會去自動釋放池將所有自動釋放對象的引用計數減一,若引用計數變成了0,則會將對象真正銷毀掉,回收內存。

所以,autorelease的對象是在每個事件循環結束後,自動釋放池才會對所有自動釋放的對象的引用計數減一,若引用計數變成了0,則釋放對象,回收內存。因此,若想要早一點釋放掉auto release對象,那麼我們可以在對象外加一個自動釋放池。比如,在循環處理數據時,臨時變量要快速釋放,就應該采用這種方式:

  for (int i = 0; i < 10000000; ++i) { @autoreleasepool { HYBTestModel *tempModel = [[HYBTestModel alloc] init]; // 臨時處理 // ... } // 出了這裡,就會去遍歷該自動釋放池了 }

5、這段代碼有問題嗎?如何修改?

  for (int i = 0; i < 10000; ++i) { NSString *str = @"Abc"; str = [str lowercaseString]; str = [str stringByAppendingString:@"xyz"]; NSLog(@"%@", str); }

參考答案:

這道題從語法上看沒有任何問題的,當然,既然面試官出了這一道題,那肯定是有問題的。

問題出在哪裡呢?語法沒有錯啊?內存最後也可以得到釋放啊!為什麼會有問題呢?是的,問題是挺大的。這對於不了解iOS的自動釋放池的原理的人或者說內存管理的人來說,這根本看不出來這有什麼問題。

問題就出在內存得不到及時地釋放。為什麼得不到及時地釋放?因為Run Loop是在每個事件循環結束後才會自動釋放池去使對象的引用計數減一,對於引用計數為0的對象才會真正被銷毀、回收內存。

因此,對於這裡的問題,一個for循環執行10000次,會產生10000個臨時自動番話對象,一直放到自動釋放池中管理,內存得不到回收。

然後,現象是內存暴漲。正確的寫法:

  for (int i = 0; i < 10000; ++i) { @autoreleasepool { NSString *str = @"Abc"; str = [str lowercaseString]; str = [str stringByAppendingString:@"xyz"]; NSLog(@"%@", str); } }

6、UIViewController的viewDidUnload、viewDidLoad和loadView分別什麼時候調用?UIView的drawRect和layoutSubviews分別起什麼作用?

參考答案:

第一個問題:

在控制器被銷毀前會調用viewDidUnload(MRC下才會調用)在控制器沒有任何view時,會調用loadView在view加載完成時,會調用viewDidLoad

第二個問題:

在調用setNeedsDisplay後,會調用drawRect方法,我們通過在此方法中可以獲取到context(設置上下文),就可以實現繪圖在調用setNeedsLayout後,會調用layoutSubviews方法,我們可以通過在此方法去調整UI。當然能引起layoutSubviews調用的方式有很多種的,比如添加子視圖、滾動scrollview、修改視圖的frame等。

7、自定義NSOperation,需要實現哪些方法?

參考答案:

對於自定義並發NSOperation,只需要實現main方法就可以了。對於自定義非並發NSOperation,需要重寫main、start、isFinished、isExecuting,還要注意在相關地方加上kvo的代碼,通知其它線程。

更多內容看這裡:iOS NSOperation

8、如何擴展ObjC裡面類的方法?

參考答案:

不太清楚題目的語義,好像是說擴展類方法?通過category很容易做到,這裡就不說了!

9、用代碼實現一個單例

參考答案:

隨手寫一個吧:

  + (instancetype)sharedInstance { static id s_manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ s_manager = [[HYBTestSingleton alloc] init]; }); return s_manager; }

10、用代碼實現一個冒泡算法

參考答案:

冒泡算法的核心算法思想是每趟兩兩比較,將小的往上浮,大的往下沉,就像氣泡一樣從水底往水面浮。

  void bubbleSort(int a[], int len) { for (int i = 0; i < len - 1; ++i) { // 從水底往水面浮,所以從最後一個開始 for (int j = len - 1; j > i; j--) { // 後者比前者還小,將需要交換 if (a[j] < a[j - 1]) { int temp = a[j]; a[j] = a[j - 1]; a[j - 1] = temp; } } } }

 

11、UITableView是如何重用cell的?

參考答案:

UITableView提供了一個屬性:visibleCells,它是記錄當前在屏幕可見的cell,要想重用cell,我們需要明確指定重用標識(identifier)。

當cell滾動出tableview可視范圍之外時,就會被放到可重用數組中。當有一個cell滾動出tableview可視范圍之外時,同樣也會有新的cell要顯示到tableview可視區,因此這個新顯示出來的cell就會先從可重用數組中通過所指定的identifier來獲取,如果能夠獲取到,則直接使用之,否則創建一個新的cell。

12、如果更高效地顯示列表

參考答案:

要更高效地顯示列表(不考慮種種優化),可以通過以下方法處理(只是部分):

提前根據數據計算好高度並緩存起來提前將數據處理、I/O計算異步處理好,並保存結果,在需要時直接拿來使用

13、Cocoa中MVC是怎麼實現的?

參考答案:

這個問題三言兩語講不明白。簡單來說,M對應於Model(數據層)、V對應於View(視圖層)、C對應於Controller(控制器層)。

如下圖:

image

用戶在V上操作,需要通過C更新M,然後將新的M交到C,C讓M更新。

14、描述KVC、KVO機制

參考答案:

KVC即是指NSKeyValueCoding,是一個非正式的Protocol,提供一種機制來間接訪問對象的屬性。KVO 就是基於KVC實現的關鍵技術之一。

KVO即Key-Value Observing,是建立在KVC之上,它能夠觀察一個對象的KVC key path值的變化。 當keypath對應的值發生變化時,會回調observeValueForKeyPath:ofObject:change:context:方法,我們可以在這裡處理。

更詳細的內容,請自行百度吧,現在筆者沒有寫相關文章!

15、使用或了解哪些設計模式

參考答案:

筆者只說說我們在開發中真正常用到的設計模式(包括架構設計模式):

單例設計模式MVC構架設計模式工廠設計模式觀察者設計模式(比如KVC/KVO/NSNotification,也有人說不是設計模式)代理設計模式

 

真心感謝標哥啊~~
  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved