京東面試題
1涣达、請寫出以下代碼輸出
int a[5] = {1, 2, 3, 4, 5};
int *ptr = (int *)(&a + 1);
printf("%d, %d", *(a + 1), *(ptr + 1));
參考答案: 2, 隨機值
這種類型題好像挺常見的瓢剿】事撸考的就是C語言上的指針的理解和數(shù)組的理解。
分析:
a代表有5個元素的數(shù)組的首地址档泽,a[5]的元素分別是1俊戳,2,3馆匿,4抑胎,5。接下來渐北,a + 1表示數(shù)據(jù)首地址加1阿逃,那么就是a[1],也就是對應(yīng)于值為2.但是,這里是&a + 1恃锉,因為a代表的是整個數(shù)組搀菩,它的空間大小為5 * sizeof(int),因此&a + 1就是a+5破托。a是個常量指針肪跋,指向當(dāng)前數(shù)組的首地址,指針+1就是移動sizeof(int)個字節(jié)土砂。
因此州既,ptr是指向int 類型的指針,而ptr指向的就是a + 5萝映,那么ptr + 1也相當(dāng)于a + 6吴叶,所以最后的(ptr + 1)就是一個隨機值了。而*(ptr – 1)就相當(dāng)于a + 4序臂,對應(yīng)的值就是5蚌卤。
2、寫一個標(biāo)準(zhǔn)宏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))
當(dāng)看到宏時,就會想到宏定義所帶來的副作用吭练。對于++诫龙、–,在宏當(dāng)中使用是最容易產(chǎn)生副作用的鲫咽,因此要慎用签赃。
分析:
p指針指向了數(shù)組array的首地址,也就是第一個元素對應(yīng)的地址分尸,其值為1.宏定義時一定要注意每個地方要加上圓括號p++相當(dāng)于p, p++,所以Max(*p++, 1)相當(dāng)于:
(p++) > (1) ? (p++) : (1) => (1) > (1) ? (p++) : (1) => 第一個p++的結(jié)果是锦聊,p所指向的值變成了2,但是1 > 1為値箩绍,所以最終max的值就是1孔庭。而后面的(*p++)也就不會執(zhí)行,因此p所指向的地址對應(yīng)的值就是2材蛛,而不是3.
擴展:如果上面的p++改成(++p)如何圆到?
分析:
(++p) > (1) ? (++p) : (1) => (2) > (1) ? (*++p) : (1) => max = *++p; => *p = 3,max = 3;
3卑吭、在一個對象的方法里:
self.name=@object;
name=@object
有什么不同
參考答案:
這是老生常談的話題了芽淡,實質(zhì)上就是問setter方法賦值與成員變量賦值有什么不同。通過點語法self.name實質(zhì)上就是[self setName:@object
];豆赏。而name這里是成員變量挣菲,直接賦值富稻。
一般來說,在對象的方法里成員變量和方法都是可以訪問的白胀,我們通常會重寫Setter方法來執(zhí)行某些額外的工作椭赋。比如說,外部傳一個模型過來或杠,那么我會直接重寫Setter方法纹份,當(dāng)模型傳過來時,也就是意味著數(shù)據(jù)發(fā)生了變化廷痘,那么視圖也需要更新顯示,則在賦值新模型的同時也去刷新UI件已。這樣也不用再額外提供其他方法了笋额。
4、怎樣使用performSelector傳入3個以上參數(shù)篷扩,其中一個為結(jié)構(gòu)體
參考答案:
- (id)performSelector:(SEL)aSelector; - (id)performSelector:(SEL)aSelector withObject:(id)object; - (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
因為系統(tǒng)提供的performSelector的api中兄猩,并沒有提供三個參數(shù)。因此鉴未,我們只能傳數(shù)組或者字典枢冤,但是數(shù)組或者字典只有存入對象類型,而結(jié)構(gòu)體并不是對象類型铜秆,那么怎么辦呢淹真?
沒有辦法,我們只能通過對象放入結(jié)構(gòu)作為屬性來傳過去了:
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
// 在堆上分配的內(nèi)存连茧,我們要手動釋放掉
- (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];
// 在回調(diào)時得到正確的數(shù)據(jù)的
- (void)call:(HYBObject *)obj {
NSLog(@"%d %d", obj.arg3->a, obj.arg3->b);
}
5核蘸、UITableViewCell上有個UILabel,顯示NSTimer實現(xiàn)的秒表時間啸驯,手指滾動cell過程中客扎,label是否刷新,為什么罚斗?
參考答案:
這是否刷新取決于timer加入到Run Loop中的Mode是什么徙鱼。Mode主要是用來指定事件在運行循環(huán)中的優(yōu)先級的,分為:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode):默認(rèn)针姿,空閑狀態(tài)UITrackingRunLoopMode:ScrollView滑動時會切換到該ModeUIInitializationRunLoopMode:run loop啟動時袱吆,會切換到該modeNSRunLoopCommonModes(kCFRunLoopCommonModes):Mode集合蘋果公開提供的Mode有兩個:
NSDefaultRunLoopMode(kCFRunLoopDefaultMode)
NSRunLoopCommonModes(kCFRunLoopCommonModes)
如果我們把一個NSTimer對象以NSDefaultRunLoopMode(kCFRunLoopDefaultMode)添加到主運行循環(huán)中的時候, ScrollView滾動過程中會因為mode的切換,而導(dǎo)致NSTimer將不再被調(diào)度搓幌。當(dāng)我們滾動的時候杆故,也希望不調(diào)度,那就應(yīng)該使用默認(rèn)模式溉愁。但是处铛,如果希望在滾動時饲趋,定時器也要回調(diào),那就應(yīng)該使用common mode撤蟆。
對于這道題奕塑,如果要cell滾動過程中定時器正常回調(diào)家肯,UI正常刷新龄砰,那么要將timer放入到CommonModes下,因為是NSDefaultRunLoopMode讨衣,只有在空閑狀態(tài)下才會回調(diào)换棚。
6、有a反镇、b固蚤、c、d 4個異步請求歹茶,如何判斷a夕玩、b、c惊豺、d都完成執(zhí)行燎孟?如果需要a、b尸昧、c揩页、d順序執(zhí)行,該如何實現(xiàn)彻磁?
參考答案:
對于這四個異步請求碍沐,要判斷都執(zhí)行完成最簡單的方式就是通過GCD的group來實現(xiàn):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, ^{ /*任務(wù)a */ });
dispatch_group_async(group, queue, ^{ /*任務(wù)b */ });
dispatch_group_async(group, queue, ^{ /*任務(wù)c */ });
dispatch_group_async(group, queue, ^{ /*任務(wù)d */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 在a、b衷蜓、c累提、d異步執(zhí)行完成后,會回調(diào)這里
});
當(dāng)然磁浇,我們還可以使用非常老套的方法來處理斋陪,通過四個變量來標(biāo)識a、b置吓、c无虚、d四個任務(wù)是否完成,然后在runloop中讓其等待衍锚,當(dāng)完成時才退出run loop友题。但是這樣做會讓后面的代碼得不到執(zhí)行,直到Run loop執(zhí)行完畢戴质。
要求順序執(zhí)行度宦,那么可以將任務(wù)放到串行隊列中踢匣,自然就是按順序來異步執(zhí)行了。
7戈抄、使用block有什么好處离唬?使用NSTimer寫出一個使用block顯示(在UILabel上)秒表的代碼。
參考答案:
說到block的好處划鸽,最直接的就是代碼緊湊输莺,傳值、回調(diào)都很方便裸诽,省去了寫代理的很多代碼嫂用。
對于這里根本沒有必要使用block來刷新UILabel顯示,因為都是直接賦值丈冬。當(dāng)然尸折,筆者覺得這是在考驗應(yīng)聘者如何將NSTimer寫成一個通用用的Block版本。
代碼放到了這里:NSTimer封裝成Block版
使用起來像這樣:
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES callback:^() {
weakSelf.secondsLabel.text = ...
}
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
8殷蛇、一個view已經(jīng)初始化完畢,view上面添加了n個button(可能使用循環(huán)創(chuàng)建)橄浓,除用view的tag之外粒梦,還可以采用什么辦法來找到自己想要的button來修改Button的值
參考答案:
這個問題有很多種方式,而且不同的使用場景也不一樣的荸实。比如說:
第一種:如果是點擊某個按鈕后匀们,才會刷新它的值,其它不用修改准给,那么不用引用任何按鈕泄朴,直接在回調(diào)時,就已經(jīng)將接收響應(yīng)的按鈕給傳過來了露氮,直接通過它修改即可祖灰。第二種:點擊某個按鈕后,所有與之同類型的按鈕都要修改值畔规,那么可以通過在創(chuàng)建按鈕時將按鈕存入到數(shù)組中局扶,在需要的時候遍歷查找。
9叁扫、tableview在滑動時三妈,有時候會大量加載本地圖片,這時候會很卡莫绣,如何解決加載耗時過長導(dǎo)致不流暢的問題
參考答案:
這是優(yōu)化tableview的相關(guān)專題畴蒲,如果只是處理圖片加載問題,那可以通過異步讀取圖片然后刷新UI对室。當(dāng)然模燥,我們也可以在取數(shù)據(jù)時咖祭,在模型中提前準(zhǔn)備好需要顯示的圖片資源,這樣在cell只就不需要操作圖片讀取涧窒,而是直接顯示心肪。
如果想要更深入地優(yōu)化,學(xué)習(xí)以下知識點:
Offscreen-RenderedColor Misaligned ImagesColor Blended Layers
10纠吴、給定一個如下的字符串(1,(2,3),(4,(5,6)7))括號內(nèi)的元素可以是數(shù)字硬鞍,也可以是括號,請實現(xiàn)一個算法清除嵌套的括號戴已,比如把上面的表達(dá)式的變成:(1,2,3,4,5,6,7)固该,表達(dá)式有誤時請報錯。
參考答案:
如果只是判斷整個表達(dá)式是否有錯誤糖儡,然后去掉里面的圓括號伐坏,那么一個循環(huán)就可以了。不過我們只需要加兩個變量分別來記錄左圓括號和右圓括號的個數(shù)握联。這里假設(shè)逗號總是正確的情況下桦沉,偽代碼如下:
left = 0;
rigt = 0;
for i = 0; i < str.length; ++i {
if 是左括號 {
left++;
continue;
}
if 是右括號 {
right++;
// 處理(1,)這樣的結(jié)構(gòu)
if 前一個是逗號 {
error;
}
continue;
}
[newStr append:str[i]];
}
if left != right { error; }
感謝標(biāo)哥的整理
優(yōu)酷面試題
1、如何聲明私有變量和私有方法金闽?
參考答案:
聲明私有變量可以通過@private關(guān)鍵字來聲明纯露。例如,這樣就是私有的成員變量了:
@interface HYBTestModel : NSObject {
@private NSString *_userName;
}
@end
沒有關(guān)鍵字聲明為私有方法代芜,因為ObjC中也沒有真正意義上的私有方法埠褪。我們要讓方法成員私有,只能通過放在.m文件中定義而不暴露在外部挤庇。但是钞速,如果有人知道內(nèi)部此這么一個方法,那么也是可以訪問的嫡秕。先說明: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方法
@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關(guān)鍵字虑凛,只能使用assign來防止循環(huán)引用碑宴。在ARC下,使用weak來防止循環(huán)引用桑谍。
4墓懂、autorelease的對象何時被釋放
參考答案:
如果了解一點點Run Loop的知道,應(yīng)該了解到:Run Loop在每個事件循環(huán)結(jié)束后會去自動釋放池將所有自動釋放對象的引用計數(shù)減一霉囚,若引用計數(shù)變成了0,則會將對象真正銷毀掉匕积,回收內(nèi)存盈罐。
所以,autorelease的對象是在每個事件循環(huán)結(jié)束后闪唆,自動釋放池才會對所有自動釋放的對象的引用計數(shù)減一盅粪,若引用計數(shù)變成了0,則釋放對象悄蕾,回收內(nèi)存票顾。因此,若想要早一點釋放掉auto release對象帆调,那么我們可以在對象外加一個自動釋放池奠骄。比如,在循環(huán)處理數(shù)據(jù)時番刊,臨時變量要快速釋放,就應(yīng)該采用這種方式:
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);
}
參考答案:
這道題從語法上看沒有任何問題的,當(dāng)然熔吗,既然面試官出了這一道題辆床,那肯定是有問題的。
問題出在哪里呢桅狠?語法沒有錯八显亍?內(nèi)存最后也可以得到釋放按谷痢维雇!為什么會有問題呢?是的晒他,問題是挺大的吱型。這對于不了解iOS的自動釋放池的原理的人或者說內(nèi)存管理的人來說,這根本看不出來這有什么問題陨仅。
問題就出在內(nèi)存得不到及時地釋放津滞。為什么得不到及時地釋放?因為Run Loop是在每個事件循環(huán)結(jié)束后才會自動釋放池去使對象的引用計數(shù)減一灼伤,對于引用計數(shù)為0的對象才會真正被銷毀触徐、回收內(nèi)存。
因此狐赡,對于這里的問題撞鹉,一個for循環(huán)執(zhí)行10000次,會產(chǎn)生10000個臨時自動番話對象颖侄,一直放到自動釋放池中管理鸟雏,內(nèi)存得不到回收。
然后览祖,現(xiàn)象是內(nèi)存暴漲孝鹊。正確的寫法:
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分別什么時候調(diào)用又活?UIView的drawRect和layoutSubviews分別起什么作用?
參考答案:
第一個問題:
在控制器被銷毀前會調(diào)用viewDidUnload(MRC下才會調(diào)用)在控制器沒有任何view時锰悼,會調(diào)用loadView在view加載完成時柳骄,會調(diào)用viewDidLoad第二個問題:
在調(diào)用setNeedsDisplay后,會調(diào)用drawRect方法箕般,我們通過在此方法中可以獲取到context(設(shè)置上下文)夹界,就可以實現(xiàn)繪圖在調(diào)用setNeedsLayout后,會調(diào)用layoutSubviews方法,我們可以通過在此方法去調(diào)整UI可柿。當(dāng)然能引起layoutSubviews調(diào)用的方式有很多種的鸠踪,比如添加子視圖、滾動scrollview复斥、修改視圖的frame等营密。
7、自定義NSOperation目锭,需要實現(xiàn)哪些方法评汰?
參考答案:
對于自定義并發(fā)NSOperation,只需要實現(xiàn)main方法就可以了痢虹。對于自定義非并發(fā)NSOperation被去,需要重寫main、start奖唯、isFinished惨缆、isExecuting,還要注意在相關(guān)地方加上kvo的代碼丰捷,通知其它線程坯墨。更多內(nèi)容看這里:iOS NSOperation
8、如何擴展ObjC里面類的方法病往?
參考答案:
不太清楚題目的語義捣染,好像是說擴展類方法?通過category很容易做到停巷,這里就不說了耍攘!
9、用代碼實現(xiàn)一個單例
參考答案:
隨手寫一個吧:
+ (instancetype)sharedInstance {
static id s_manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
s_manager = [[HYBTestSingleton alloc] init]; });
return s_manager;
}
}
10畔勤、用代碼實現(xiàn)一個冒泡算法
參考答案:
冒泡算法的核心算法思想是每趟兩兩比較蕾各,將小的往上浮,大的往下沉硼被,就像氣泡一樣從水底往水面浮。
-(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仔掸,它是記錄當(dāng)前在屏幕可見的cell,要想重用cell医清,我們需要明確指定重用標(biāo)識(identifier)起暮。
當(dāng)cell滾動出tableview可視范圍之外時,就會被放到可重用數(shù)組中会烙。當(dāng)有一個cell滾動出tableview可視范圍之外時负懦,同樣也會有新的cell要顯示到tableview可視區(qū)筒捺,因此這個新顯示出來的cell就會先從可重用數(shù)組中通過所指定的identifier來獲取,如果能夠獲取到纸厉,則直接使用之系吭,否則創(chuàng)建一個新的cell。
12颗品、如果更高效地顯示列表
參考答案:
要更高效地顯示列表(不考慮種種優(yōu)化)肯尺,可以通過以下方法處理(只是部分):
提前根據(jù)數(shù)據(jù)計算好高度并緩存起來提前將數(shù)據(jù)處理、I/O計算異步處理好躯枢,并保存結(jié)果则吟,在需要時直接拿來使用
13、Cocoa中MVC是怎么實現(xiàn)的锄蹂?
參考答案:
這個問題三言兩語講不明白氓仲。簡單來說,M對應(yīng)于Model(數(shù)據(jù)層)败匹、V對應(yīng)于View(視圖層)寨昙、C對應(yīng)于Controller(控制器層)。
如下圖:
用戶在V上操作掀亩,需要通過C更新M舔哪,然后將新的M交到C,C讓M更新槽棍。
14捉蚤、描述KVC、KVO機制
參考答案:
KVC即是指NSKeyValueCoding炼七,是一個非正式的Protocol缆巧,提供一種機制來間接訪問對象的屬性。KVO 就是基于KVC實現(xiàn)的關(guān)鍵技術(shù)之一豌拙。
KVO即Key-Value Observing陕悬,是建立在KVC之上,它能夠觀察一個對象的KVC key path值的變化按傅。 當(dāng)keypath對應(yīng)的值發(fā)生變化時捉超,會回調(diào)observeValueForKeyPath:ofObject:change:context:方法,我們可以在這里處理唯绍。
更詳細(xì)的內(nèi)容拼岳,請自行百度吧,現(xiàn)在筆者沒有寫相關(guān)文章况芒!
15惜纸、使用或了解哪些設(shè)計模式
參考答案:
筆者只說說我們在開發(fā)中真正常用到的設(shè)計模式(包括架構(gòu)設(shè)計模式):
單例設(shè)計模式MVC構(gòu)架設(shè)計模式工廠設(shè)計模式觀察者設(shè)計模式(比如KVC/KVO/NSNotification,也有人說不是設(shè)計模式)代理設(shè)計模式