綜合面試題(答案)

1. 單例寫法

單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。

  • 一般情況下, 如果一個類是單例, 那么都會提供一個類方法用于快速創(chuàng)建單例對象蝌麸;
  • 而且這個類方法的名稱是有一定的規(guī)則: share + 類名稱 / default + 類名稱 / 類名稱開頭。
//GCD 方式創(chuàng)建
static id _instance;   

+ (instancetype)sharedInstance  
{   
    static dispatch_once_t onceToken;   
    dispatch_once(&onceToken, ^{   
        _instance = [[self alloc] init];   
    });   
    return _instance;   
}   

+ (instancetype)allocWithZone:(struct _NSZone *)zone  
{   
    static dispatch_once_t onceToken;   
    dispatch_once(&onceToken, ^{   
        _instance = [super allocWithZone:zone];   
    });   
    return _instance;   
}   

- (id)copyWithZone:(NSZone *)zone   
{   
    return _instance;   
}  

- (id)mutableCopyWithZone:(NSZone *)zone {   
    return _instance;   
}
2. 深拷貝淺拷貝 舉出使用實例

iOS提供了copy和mutablecopy方法弟疆,顧名思義,copy就是復制了一個imutable的對象柑司,而mutablecopy就是復制了一個mutable的對象。

  • 系統(tǒng)的非容器類對象(這里指的是NSString,NSNumber等一類的對象)
    • 如果對一不可變對象復制讼育,copy是淺拷貝,mutableCopy就是深拷貝。
    • 如果是對可變對象復制蹲缠,都是深拷貝娜谊,但是copy返回的對象是不可變的纱皆。
  • 系統(tǒng)的容器類對象
    • 對于容器類本身,上面討論的結(jié)論也是適用的,需要探討的是復制后容器內(nèi)對象的變化
    • 容器內(nèi)的元素內(nèi)容都是指針復制
3.@property 相關(guān)問題總匯:

實例變量+基本數(shù)據(jù)類型變量=成員變量
屬性 (property)有兩大概念:ivar(實例變量)+存取方法(getter + setter)

3.1 ARC下钳踊,不顯式指定任何屬性關(guān)鍵字時助琐,默認的關(guān)鍵字都有哪些
  • 對應(yīng)基本數(shù)據(jù)類型默認關(guān)鍵字是:
    atomic,readwrite,assign
  • 對于普通的OC對象:
    atomic,readwrite,strong
3.2 什么情況使用 weak 關(guān)鍵字兵钮,相比 assign 有什么不同
  • 什么情況使用 weak 關(guān)鍵字呻拌?
    • 在ARC中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用weak來解決,比如:delegate代理屬性
    • 自身已經(jīng)對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak, 自定義View的子控件屬性一般也使用weak;但是也可以使用strong
  • 不同點:
    • weak當對象銷毀的時候,指針會被自動設(shè)置為nil,而assign不會
    • assigin 可以用非OC對象,而weak必須用于OC對象
3.3 NSString 屬性什么時候用copy靴拱,什么時候用strong(ARC環(huán)境)

我們定義一個類,并為其聲明兩個字符串屬性偎窘,如下所示:

@interface TestStringClass ()
@property (nonatomic, strong) NSString *strongString;
@property (nonatomic, copy) NSString *copyedString;
@end

首先眷茁,我們用一個不可變字符串來為這兩個屬性賦值,

- (void)test {
    NSString *string = [NSString stringWithFormat:@"abc"];
    self.strongString = string;
    self.copyedString = string;
    NSLog(@"origin string: %p, %p", string, &string);
    NSLog(@"strong string: %p, %p", _strongString, &_strongString);
    NSLog(@"copy string: %p, %p", _copyedString, &_copyedString);
}

其輸出結(jié)果是:

origin string: 0x7fe441592e20, 0x7fff57519a48
strong string: 0x7fe441592e20, 0x7fe44159e1f8
copy string: 0x7fe441592e20, 0x7fe44159e200

我們可以看到登刺,這種情況下南窗,不管是strong還是copy屬性的對象窒悔,其指向的地址都是同一個,即為string指向的地址。如果我們換作MRC環(huán)境祭玉,打印string的引用計數(shù)的話,會看到其引用計數(shù)值是3,即strong操作和copy操作都使原字符串對象的引用計數(shù)值加了1铺韧。
接下來塔逃,我們把string由不可變改為可變對象,看看會是什么結(jié)果。即將下面這一句

NSString *string = [NSString stringWithFormat:@"abc"];

改成:

NSMutableString *string = [NSMutableString stringWithFormat:@"abc"];

其輸出結(jié)果是:

origin string: 0x7ff5f2e33c90, 0x7fff59937a48
strong string: 0x7ff5f2e33c90, 0x7ff5f2e2aec8
copy string: 0x7ff5f2e2aee0, 0x7ff5f2e2aed0

可以發(fā)現(xiàn),此時copy屬性字符串已不再指向string字符串對象胜卤,而是深拷貝了string字符串澈段,并讓_copyedString對象指向這個字符串芒率。在MRC環(huán)境下德玫,打印兩者的引用計數(shù),可以看到string對象的引用計數(shù)是2,而_copyedString對象的引用計數(shù)是1显熏。

此時,我們?nèi)绻バ薷膕tring字符串的話,可以看到:因為_strongString與string是指向同一對象歧寺,所以_strongString的值也會跟隨著改變(需要注意的是,此時_strongString的類型實際上是NSMutableString棘脐,而不是NSString)斜筐;而_copyedString是指向另一個對象的,所以并不會改變荆残。
結(jié)論 由于NSMutableString是NSString的子類奴艾,所以一個NSString指針可以指向NSMutableString對象——讓我們的strongString指針指向一個可變字符串是可以的。
當源字符串是NSString時:由于字符串是不可變的,所以遮婶,不管是strong還是copy屬性的對象,都是指向源對象,copy操作只是做了次淺拷貝。
當源字符串是NSMutableString時:strong屬性只是增加了源字符串的引用計數(shù)猜年,而copy屬性則是對源字符串做了次深拷貝,產(chǎn)生一個新的對象稳析,且copy屬性對象指向這個新的對象。另外需要注意的是平委,這個copy屬性對象的類型始終是NSString劳曹,而不是NSMutableString溉浙,因此其是不可變的吼渡。
這里還有一個性能問題,即在源字符串是NSMutableString命黔,strong是單純的增加對象的引用計數(shù)隅忿,而copy操作是執(zhí)行了一次深拷貝,所以性能上會有所差異。而如果源字符串是NSString時,則沒有這個問題。
所以坤候,在聲明NSString屬性時尼酿,到底是選擇strong還是copy,可以根據(jù)實際情況來定。不過拉岁,一般我們將對象聲明為NSString時,都不希望它改變惰爬,所以大多數(shù)情況下喊暖,建議使用copy,以免因可變字符串的修改導致的一些非預期問題撕瞧。

3.4 用@property聲明的NSString(或NSArray陵叽,NSDictionary)經(jīng)常使用copy關(guān)鍵字狞尔,為什么?如果改用strong關(guān)鍵字咨跌,可能造成什么問題沪么?

因為父類指針可以指向子類對象NSMutableString、NSMutableArray锌半、NSMutableDictionary,他們之間可能進行賦值操作寇漫。使用copy的目的是為了讓本對象的屬性不受外界影響,使用copy無論給我傳入是一個可變對象還是不可變對象刊殉,我本身持有的就是一個不可變的副本。
如果我們使用是strong,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性州胳。

copy此特質(zhì)所表達的所屬關(guān)系與strong類似记焊。然而設(shè)置方法并不保留新值,而是將其“拷貝” (copy)栓撞。
當屬性類型為NSString時遍膜,經(jīng)常用此特質(zhì)來保護其封裝性,因為傳遞給設(shè)置方法的新值有可能指向一個NSMutableString類的實例瓤湘,這個類是NSString的子類瓢颅,表示一種可修改其值的字符串,此時若是不拷貝字符串弛说,那么設(shè)置完屬性之后挽懦,字符串的值就可能會在對象不知情的情況下遭人更改。所以木人,這時就要拷貝一份“不可變” 的字符串信柿,確保對象中的字符串值不會無意間變動。只要實現(xiàn)屬性所用的對象是“可變的” 醒第,就應(yīng)該在設(shè)置新屬性值時拷貝一份渔嚷。

3.5 這個寫法會出什么問題: @property (copy) NSMutableArray *array;

兩個問題:
1、添加,刪除,修改數(shù)組內(nèi)的元素的時候,程序會因為找不到對應(yīng)的方法而崩潰.因為copy就是復制一個不可變NSArray的對象稠曼;
2形病、使用了atomic屬性會嚴重影響性能。

4. #include #import @class

#includeC/C++導入頭文件的關(guān)鍵字
#importObjective-c導入頭文件的關(guān)鍵字蒲列,,使用#import頭文件會自動只導入一次窒朋,不會重復導入,相當于#include#pragma once蝗岖;#import<>用來包含系統(tǒng)的頭文件侥猩,#import””用來包含用戶頭文件。
@class:告訴編譯器某個類的聲明抵赢,當執(zhí)行時欺劳,才去查看類的實現(xiàn)文件唧取,可以解決頭文件的相互包含

5.循環(huán)引用相關(guān)問題匯總:

循環(huán)引用可以簡單理解為A引用了B划提,而B又引用了A枫弟,雙方都同時保持對方的一個引用,導致任何時候引用計數(shù)都不為0鹏往,始終無法釋放淡诗。若當前對象是一個ViewController,則在dismiss或者pop之后其dealloc無法被調(diào)用伊履,在頻繁的push或者present之后內(nèi)存暴增韩容,然后APP就掛了

  • block的使用有時會導致循環(huán)引用
  • 定時器的使用有時會導致循環(huán)引用
5.1 使用block時什么情況會發(fā)生引用循環(huán),如何解決

一個對象中強引用了block唐瀑,在block中又使用了該對象群凶,就會發(fā)射循環(huán)引用。 解決方法是將該對象使用 _weak或者_block修飾符修飾之后再在block中使用哄辣。
id weak weakSelf = self; 或者 __weak typeof(self) weakSelf = self;該方法可以設(shè)置宏
id __block weakSelf = self;

5.2 使用系統(tǒng)的某些block api(如UIView的block版本寫動畫時)请梢,是否也考慮引用循環(huán)問題?

系統(tǒng)的某些block api中力穗,UIView的block版本寫動畫時不需要考慮毅弧,但也有一些api 需要考慮:

所謂“引用循環(huán)”是指雙向的強引用,所以那些“單向的強引用”(block 強引用 self )沒有問題睛廊,比如這些:
1.
[UIView animateWithDuration:duration animations:^{ 
    [self.superview layoutIfNeeded]; 
}]; 
2.
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
    self.someProperty = xyz; 
}]; 
3.
[[NSNotificationCenter defaultCenter] addObserverForName:@"someNotification" 
                                                  object:nil 
                                                   queue:[NSOperationQueue mainQueue]                                                   
                                              usingBlock:^(NSNotification * notification) {
                                                    self.someProperty = xyz; 
                                               }];
這些情況不需要考慮“引用循環(huán)”形真。

但如果你使用一些參數(shù)中可能含有實例變量的系統(tǒng) api 如 GCD 、NSNotificationCenter就要小心一點:

比如GCD 內(nèi)部如果引用了 self超全,而且 GCD 的其他參數(shù)是實例變量咆霜,則要考慮到循環(huán)引用:
1.
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
    __typeof__(self) strongSelf = weakSelf;
    [strongSelf doSomething];
    [strongSelf doSomethingElse];
} );

類似的:
2.
__weak __typeof__(self) weakSelf = self;
  _observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"testKey"
                                                    object:nil
                                                    queue:nil
                                                    usingBlock:^(NSNotification *note) {
                                    __typeof__(self) strongSelf = weakSelf;
                                   [strongSelf dismissModalViewControllerAnimated:YES];
  }];
self --> _observer --> block --> self 顯然這也是一個循環(huán)引用。

這篇文章介紹了weakself和strongself的用法:到底什么時候才需要在ObjC的Block中使用weakSelf/strongSelf

5.3 NSTimer導致循環(huán)引用的例子

一方面嘶朱,NSTimer經(jīng)常會被作為某個類的成員變量蛾坯,而NSTimer初始化時要指定self為target,容易造成循環(huán)引用疏遏。
另一方面脉课,若timer一直處于validate的狀態(tài),則其引用計數(shù)將始終大于0财异。比如當定時器銷毀的時機不對倘零,在dealloc里面銷毀的時候,內(nèi)存就不會釋放,就會造成循環(huán)引用

.h文件
#import <Foundation/Foundation.h>
@interface Friend : NSObject
{
    NSTimer *_timer;
}
- (void)cleanTimer;
@end

.m文件
@implementation Friend
- (id)init
{
    if (self = [super init]) {
     _timer = [NSTimer scheduledTimerWithTimeInterval:1 
                       target:self 
                       selector:@selector(handleTimer:)
                       userInfo:nil 
                       repeats:YES];
     }
    return  self;
 } 
- (void)handleTimer:(id)sender
{
   NSLog(@"%@ say: Hi!", [self class]);
 }
- (void)cleanTimer
{
   [_timer invalidate];
    _timer = nil;
}
 - (void)dealloc
 {
   [self cleanTimer];
  NSLog(@"[Friend class] is dealloced");
 }

在main.m中聲明并且調(diào)用戳寸,通過函數(shù)讓Friend類延時5秒后引用計數(shù)減一

#import "Friend.h"
//循環(huán)引用
//是一個很麻煩的一件事呈驶,完全靠經(jīng)驗
int main(int argc, const char * argv[]) {   
     Friend *friend = [[Friend alloc] init];
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 95*NSEC_PER_SEC), 
                    dispatch_get_main_queue(), ^{
                    [friend release];
    });
    return 0;
 }
我們所期待的結(jié)果是,初始化5秒后疫鹊,friend對象被release袖瞻,friend的dealloc方法被調(diào)用司致,在dealloc里面timer失效,對象被析構(gòu)聋迎。但結(jié)果卻是從未停下..

這是為什么呢脂矫?主要是因為從timer的角度,timer認為調(diào)用方(Friend對象)被析構(gòu)時會進入dealloc霉晕,在dealloc可以順便將timer的計時停掉并且釋放內(nèi)存庭再;但是從Friend的角度,他認為timer不停止計時不析構(gòu)牺堰,那我永遠沒機會進入dealloc佩微。循環(huán)引用,互相等待萌焰,無窮盡。問題的癥結(jié)在于-(void)cleanTimer函數(shù)的調(diào)用時機不對谷浅,顯然不能想當然地放在調(diào)用者的dealloc中扒俯。

一個比較好的解決方法是開放這個函數(shù),讓Friend的調(diào)用者顯式地調(diào)用來清理現(xiàn)場一疯。如下:

int main(int argc, const char * argv[]) {   
     Friend *friend = [[Friend alloc] init];
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 95*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
             [friend cleanTimer];
             [friend release];
    });
    return 0;
 }
6.UIView和CALayer的區(qū)別與聯(lián)系
  • 每個 UIView 內(nèi)部都有一個 CALayer 在背后提供內(nèi)容的繪制和顯示撼玄,UIView的尺寸樣式都由內(nèi)部的 Layer 所提供。兩者都有樹狀層級結(jié)構(gòu)墩邀,layer 內(nèi)部有 SubLayers掌猛,View內(nèi)部有SubViews。但是Layer比View多了個AnchorPoint眉睹。
  • 在View顯示的時候荔茬,UIView做為Layer的CALayerDelegate,View的顯示內(nèi)容由內(nèi)部的CALayer的display竹海。
  • CALayer修改屬性是支持隱式動畫的慕蔚,在給UIView的Layer做動畫的時候,View作為Layer的代理斋配,Layer向View請求相應(yīng)的action(動畫行為)
  • layer 內(nèi)部維護著三份 layer tree孔飒,分別是 :
    presentLayer Tree(動畫樹)
    modeLayer Tree(模型樹)
    Render Tree (渲染樹)。
    在做iOS動畫的時候艰争,我們修改動畫的屬性坏瞄,在動畫的其實是Layer的presentLayer屬性值,而最終展示在界面上的其實是提供View的modelLayer
  • 兩者最明顯的區(qū)別是View可以接受并處理事件甩卓,而Layer不可以
7.詳細描述一下響應(yīng)者鏈的含義(最好能圖釋)

iOS 系統(tǒng)檢測到手指觸摸 (Touch) 操作時會將其打包成一個 UIEvent 對象鸠匀,并放入當前活動Application的事件隊列,單例UIApplication會從事件隊列中取出觸摸事件并傳遞給單例UIWindow來處理猛频,UIWindow對象首先會使用hitTest:withEvent:方法尋找此次Touch操作初始點所在的視圖(View)狮崩,即需要將觸摸事件傳遞給其處理的視圖蛛勉,這個過程稱之為hit-test view。
hitTest:withEvent:方法的處理流程如下:

  • 首先調(diào)用當前視圖的pointInside:withEvent: 方法判斷觸摸點是否在當前視圖內(nèi)睦柴;
  • 若返回NO, 則hitTest:withEvent: 返回 nil诽凌,若返回YES, 則向當前視圖的所有子視圖 (subviews) 發(fā)送 hitTest:withEvent: 消息,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖坦敌,即從subviews數(shù)組的末尾向前遍歷侣诵,直到有子視圖返回非空對象或者全部子視圖遍歷完畢;
  • 若第一次有子視圖返回非空對象狱窘,則 hitTest:withEvent: 方法返回此對象杜顺,處理結(jié)束;
  • 如所有子視圖都返回空蘸炸,則 hitTest:withEvent: 方法返回自身 (self)躬络。

如果最終 hit-test 沒有找到第一響應(yīng)者,或者第一響應(yīng)者沒有處理該事件搭儒,則該事件會沿著響應(yīng)者鏈向上回溯穷当,如果 UIWindow 實例和 UIApplication 實例都不能處理該事件,則該事件會被丟棄(這個過程即上面提到的響應(yīng)值鏈)淹禾;

8.如何高效的剪切圓角圖片

CALayer 的 border馁菜、圓角、陰影铃岔、遮罩(mask)汪疮,CASharpLayer 的矢量圖形顯示,通常會觸發(fā)離屏渲染(offscreen rendering)毁习,而離屏渲染通常發(fā)生在 GPU 中智嚷。當一個列表視圖中出現(xiàn)大量圓角的 CALayer,并且快速滑動時蜓洪,可以觀察到 GPU 資源已經(jīng)占滿纤勒,而 CPU 資源消耗很少。這時界面仍然能正陈√矗滑動摇天,但平均幀數(shù)會降到很低。為了避免這種情況恐仑,可以嘗試開啟 CALayer.shouldRasterize 屬性泉坐,但這會把原本離屏渲染的操作轉(zhuǎn)嫁到 CPU 上去。對于只需要圓角的某些場合裳仆,也可以用一張已經(jīng)繪制好的圓角圖片覆蓋到原本視圖上面來模擬相同的視覺效果腕让。最徹底的解決辦法,就是把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角纯丸、陰影偏形、遮罩等屬性。

9.const/static分別用法觉鼻,修飾類時又如何 #define
  • static
    • 函數(shù)體內(nèi) static 變量的作用范圍為該函數(shù)體俊扭,該變量的內(nèi)存只被分配一次,因此其值在下次調(diào)用時仍維持上次的值坠陈;
    • 在模塊內(nèi)的 static "全局變量"可以被模塊內(nèi)所有函數(shù)訪問萨惑,但不能被模塊外其它函數(shù)訪問;
    • 在模塊內(nèi)的 static "函數(shù)"只可被這一模塊內(nèi)的其它函數(shù)調(diào)用仇矾,這個函數(shù)的使用范圍被限制在聲明它的模塊內(nèi)庸蔼;
    • 在類中的 static "成員變量"屬于整個類所擁有,對類的所有對象只有一份拷貝贮匕;
    • 在類中的 static "成員函數(shù)"屬于整個類所擁有姐仅,這個函數(shù)不接收 this 指針,因而只能訪問類的static 成員變量刻盐。

宏:

#define HSCoder @"漢斯哈哈哈"

變量:

NSString *HSCoder = @"漢斯哈哈哈";

常量:

四種寫法:
static const NSString *HSCoder = @"漢斯哈哈哈";

const NSString *HSCoder = @"漢斯哈哈哈";   //"*HSCoder"不能被修改萍嬉, "HSCoder"能被修改
NSString const *HSCoder = @"漢斯哈哈哈";   //"*HSCoder"不能被修改, "HSCoder"能被修改(與上個沒啥區(qū)別)
NSString * const HSCoder = @"漢斯哈哈哈";  //"HSCoder"不能被修改隙疚,"*HSCoder"能被修改

結(jié)論:const右邊的總不能被修改

所以一般我們定義一個常量又不想被修改應(yīng)該選擇最后一種方案:

NSString * const HSCoder = @"漢斯哈哈哈";

.h文件中這樣寫:

UIKIT_EXTERN NSString *const HSCoder
10.如何使用Objective-C實現(xiàn)多重繼承?并給出代碼示例
  • 通過組合實現(xiàn)“多繼承”
  • 通過協(xié)議實現(xiàn)“多繼承”
  • 通過category實現(xiàn)“單繼承”(大部分網(wǎng)上文章將此方法誤解成“多繼承”)
11.關(guān)于app性能優(yōu)化你是怎么理解的磕道?請詳細描述一下
  • 入門級(這是些你一定會經(jīng)常用在你app開發(fā)中的建議,8個)
    • 用ARC管理內(nèi)存
    • 不要block主線程
    • 打開gzip壓縮
    • 在正確的地方使用reuseIdentifier
      自iOS6起供屉,除了UICollectionView的cells和補充views,也應(yīng)該在header和footer views中使用reuseIdentifiers伶丐。
    • 盡可能使Views不透明, 避免圖層混合
      確保控件的opaque屬性設(shè)置為true疯特,確保backgroundColor和父視圖顏色一致且不透明
      如無特殊需要邻吞,不要設(shè)置低于1的alpha值
      確保UIImage沒有alpha通道
    • 避免龐大的XIB
      如果你不得不XIB的話抱冷,使他們盡量簡單旺遮。嘗試為每個Controller配置一個單獨的XIB赵讯,盡可能把一個View Controller的view層次結(jié)構(gòu)分散到單獨的XIB中去盈咳。
      需要注意的是,當你加載一個XIB的時候所有內(nèi)容都被放在了內(nèi)存里边翼,包括任何圖片鱼响。如果有一個不會即刻用到的view,你這就是在浪費寶貴的內(nèi)存資源了讯私。Storyboards就是另一碼事兒了热押,storyboard僅在需要時實例化一個view controller.
    • 在Image Views中調(diào)整圖片大小,避免臨時轉(zhuǎn)換
      確保圖片大小和frame一致,不要在滑動時縮放圖片
      確保圖片顏色格式被GPU支持斤寇,避免勞煩CPU轉(zhuǎn)換
    • 選擇正確的Collection
      Arrays: 有序的一組值桶癣。使用index來lookup很快,使用value lookup很慢娘锁, 插入/刪除很慢牙寞。
      Dictionaries: 存儲鍵值對。 用鍵來查找比較快莫秆。
      Sets: 無序的一組值间雀。用值來查找很快,插入/刪除很快镊屎。
  • 中級(這些是你可能在一些相對復雜情況下可能用到的)
    • 權(quán)衡渲染方法

    • 優(yōu)化你的Table View

    • 重用和延遲加載Views
      這里我們用到的技巧就是模仿UITableView和UICollectionView的操作: 不要一次創(chuàng)建所有的subview惹挟,而是當需要時才創(chuàng)建,當它們完成了使命缝驳,把他們放進一個可重用的隊列中连锯。

    • Cache, Cache, 還是Cache!
      NSCache和NSDictionary類似用狱,不同的是系統(tǒng)回收內(nèi)存的時候它會自動刪掉它的內(nèi)容唾那。

    • 處理內(nèi)存警告
      如果你的app收到了內(nèi)存警告觉壶,它就需要盡可能釋放更多的內(nèi)存官觅。最佳方式是移除對緩存刻肄,圖片object和其他一些可以重創(chuàng)建的objects的strong references.
      UIKit提供了幾種收集低內(nèi)存警告的方法:

      • 在app delegate中使用applicationDidReceiveMemoryWarning: 的方法
      • 在你的自定義UIViewController的子類(subclass)中覆蓋didReceiveMemoryWarning
      • 注冊并接收 UIApplicationDidReceiveMemoryWarningNotification 的通知
    • 避免反復處理數(shù)據(jù),選擇正確的數(shù)據(jù)格式
      如需要數(shù)據(jù)來展示一個table view,最好直接從服務(wù)器取array結(jié)構(gòu)的數(shù)據(jù)以避免額外的中間數(shù)據(jù)結(jié)構(gòu)改變溺忧。
      如需要從特定key中取數(shù)據(jù)咏连,那么就使用鍵值對的dictionary。

    • 正確地設(shè)定Background Images

    • 如果你使用全畫幅的背景圖鲁森,你就必須使用UIImageView因為UIColor的colorWithPatternImage是用來創(chuàng)建小的重復的圖片作為背景的捻勉。這種情形下使用UIImageView可以節(jié)約不少的內(nèi)存

    • 如果你用小圖平鋪來創(chuàng)建背景,你就需要用UIColor的colorWithPatternImage來做了刀森,它會更快地渲染也不會花費很多內(nèi)存

    • 重用大開銷的對象
      一些objects的初始化很慢踱启,比如NSDateFormatter和NSCalendar,想要避免使用這個對象的瓶頸你就需要重用他們,可以通過1.添加屬性到你的class里 或者2.創(chuàng)建靜態(tài)變量來實現(xiàn)埠偿。
      如果你要選擇第二種方法透罢,對象會在你的app運行時一直存在于內(nèi)存中,和單例(singleton)很相似冠蒋。
      下面的代碼說明了使用一個屬性來延遲加載一個date formatter. 第一次調(diào)用時它會創(chuàng)建一個新的實例羽圃,以后的調(diào)用則將返回已經(jīng)創(chuàng)建的實例:

// in your .h or inside a class extension
@property (nonatomic, strong) NSDateFormatter *formatter;

// inside the implementation (.m)
// When you need, just use self.formatter
- (NSDateFormatter *)formatter {
    if (! _formatter) {
        _formatter = [[NSDateFormatter alloc] init];
        _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format
    }
    return _formatter;
}
  • 進階級(這些建議只應(yīng)該在你確信他們可以解決問題和得心應(yīng)手的情況下采用)

    • 加速啟動時間
    • 使用Autorelease Pool
    • 選擇是否緩存圖片
    • 盡量避免日期格式轉(zhuǎn)換
  • UIKit方面

    • 慎用離屏渲染
      絕大多數(shù)時候離屏渲染會影響性能
      重寫drawRect方法,設(shè)置圓角抖剿、陰影朽寞、模糊效果,光柵化都會導致離屏渲染
      設(shè)置陰影效果是加上陰影路徑
      滑動時若需要圓角效果斩郎,開啟光柵化
  • drawrect
    繪制圖形性能的優(yōu)化最好辦法就是不去繪制脑融。
    利用專有圖層代替繪圖需求。
    不得不用到繪圖盡量縮小視圖面積缩宜,并且盡量降低重繪頻率肘迎。
    異步繪制,推測內(nèi)容锻煌,提前在其他線程繪制圖片妓布,在主線程中直接設(shè)置圖片。

12. tableview優(yōu)化

為了保證table view平滑滾動宋梧,確保你采取了以下的措施:

  • 正確使用reuseIdentifier來重用cells
  • 盡量使所有的view opaque匣沼,包括cell自身
  • 避免漸變,圖片縮放
  • 緩存行高
  • 如果cell內(nèi)現(xiàn)實的內(nèi)容來自web捂龄,使用異步加載肛著,緩存請求結(jié)果
  • 使用shadowPath來畫陰影
  • 減少subviews的數(shù)量
  • 盡量不適用cellForRowAtIndexPath:,如果你需要用到它跺讯,只用一次然后緩存結(jié)果
  • 使用正確的數(shù)據(jù)結(jié)構(gòu)來存儲數(shù)據(jù)
  • 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設(shè)定固定的高,不要請求delegate
13. 對象沒銷毀的原因有哪些
  • 控制器中NSTimer沒有被銷毀
    當控制器中存在NSTimer時殉农,就需要注意刀脏,因為當
[NSTimer scheduledTimerWithTimeInterval:1.0 
           target:self 
           selector:@selector(updateTime:) 
           userInfo:nil 
           repeats:YES];

時,這個target:self就增加了VC的RetainCount超凳,如果你不將這個timer invalidate愈污,就別想調(diào)用dealloc。需要在viewWillDisappear之前需要把控制器用到的NSTimer銷毀轮傍。

[timer invalidate]; // 銷毀timer
timer = nil; // 置nil
  • 控制器中的代理不是weak屬性
    例如@property (nonatomic, weak) id<HCAppViewDelegate> delegate;代理要使用弱引用暂雹,因為自定義控件是加載在視圖控制器中的,視圖控制器view對自定義控件是強引用创夜,如果代理屬性設(shè)置為strong杭跪,則意味著delegate對視圖控制器也進行了強引用,會造成循環(huán)引用。導致控制器無法被釋放涧尿,最終導致內(nèi)存泄漏系奉。
  • 控制器中block的循環(huán)引用
14.多線程操作
  • 14.1 多線程處理方式及優(yōu)缺
  • pThread c語言框架
    一套通用的多線程API,適用于Linux\Windows\Unix,跨平臺姑廉,可移植缺亮,使用C語言,生命周期需要程序員管理桥言,IOS開發(fā)中使用很少萌踱。
  • NSThread apple 封裝過,面向?qū)ο蠛虐ⅲ芍苯硬倏鼐€程對象
    優(yōu)點:NSThread 比其他兩個輕量級并鸵。
    缺點:需要自己管理線程的生命周期,線程同步倦西。線程同步對數(shù)據(jù)的加鎖會有一定的系統(tǒng)開銷能真。
1.先創(chuàng)建線程類,再啟動
NSThread *thread = [[NSThread alloc] initWithTarget:self 
                                     selector:@selector(run:) 
                                     object:nil];
[thread start];

2.創(chuàng)建并自動啟動
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];

3.使用 NSObject 的方法創(chuàng)建并自動啟動
[self performSelectorInBackground:@selector(run:) withObject:nil];
  • GCD為多核并行運算提出的解決方案扰柠,自動管理線程的生命周期(創(chuàng)建線程粉铐、調(diào)度任務(wù)、銷毀線程)
    • 任務(wù):即操作卤档,你想要干什么蝙泼,說白了就是一段代碼,在 GCD 中就是一個 Block劝枣,所以添加任務(wù)十分方便汤踏。任務(wù)有兩種執(zhí)行方式: 同步執(zhí)行 和 異步執(zhí)行,他們之間主要區(qū)別在于會不會阻塞當前線程舔腾,直到 Block 中的任務(wù)執(zhí)行完畢溪胶!
      • 同步(sync) 操作,它會阻塞當前線程并等待 Block 中的任務(wù)執(zhí)行完畢稳诚,然后當前線程才會繼續(xù)往下運行哗脖。
      • 異步(async)操作,當前線程會直接往下執(zhí)行扳还,它不會阻塞當前線程才避。
    • 隊列:用于存放任務(wù)。一共有兩種隊列氨距, 串行隊列 和 并行隊列桑逝。
      • 串行隊列:GCD 會 FIFO(先進先出) 地取出來一個,執(zhí)行一個俏让,然后取下一個楞遏,這樣一個一個的執(zhí)行茬暇。
      • 并行隊列:放到并行隊列的任務(wù),GCD 也會 FIFO的取出來橱健,但不同的是而钞,它取出來一個就會放到別的線程,然后再取出來一個又放到另一個的線程拘荡。這樣由于取的動作很快臼节,忽略不計,看起來珊皿,所有的任務(wù)都是一起執(zhí)行的网缝。不過需要注意,GCD 會根據(jù)系統(tǒng)資源控制并行的數(shù)量蟋定,所以如果任務(wù)很多粉臊,它并不會讓所有任務(wù)同時執(zhí)行。
創(chuàng)建隊列
1.主隊列:這是一個特殊的 `串行隊列`
dispatch_queue_t queue = dispatch_get_main_queue();
2.全局并行隊列:只要是并行任務(wù)一般都加入到這個隊列驶兜。這是系統(tǒng)提供的一個并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3.自己創(chuàng)建的隊列:
 //串行隊列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
  //并行隊列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);

創(chuàng)建任務(wù)
1.同步任務(wù): 會阻塞當前線程 (SYNC)
 dispatch_sync(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
2.異步任務(wù):不會阻塞當前線程 (ASYNC)
dispatch_async(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

隊列組
//1.創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//2.創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//3.多次使用隊列組的方法執(zhí)行任務(wù), 只有異步方法
//3.1.執(zhí)行3次循環(huán)
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 3; i++) {
        NSLog(@"group-01 - %@", [NSThread currentThread]);
    }
});

//3.2.主隊列執(zhí)行8次循環(huán)
dispatch_group_async(group, dispatch_get_main_queue(), ^{
    for (NSInteger i = 0; i < 8; i++) {
        NSLog(@"group-02 - %@", [NSThread currentThread]);
    }
});

//3.3.執(zhí)行5次循環(huán)
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 5; i++) {
        NSLog(@"group-03 - %@", [NSThread currentThread]);
    }
});

//4.都完成后會自動通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"完成 - %@", [NSThread currentThread]);
});
  • NSOperation GCD的封裝 它的實例封裝了需要的操作以及操作所需要的數(shù)據(jù)
    優(yōu)點:不需要關(guān)心線程管理扼仲, 數(shù)據(jù)同步的事情,可以把精力放在自己需要執(zhí)行的操作上抄淑。NSOperation是個抽象類,可以使用它定義好的兩個子類: NSInvocationOperationNSBlockOperation屠凶,或者用它的子類(比較高級,暫不提)肆资,創(chuàng)建NSOperation子類的對象矗愧。
    NSOperationNSOperationQueue 分別對應(yīng) GCD 的 任務(wù)隊列
添加任務(wù)
1.NSInvocationOperation : 需要傳入一個方法名
  //1.創(chuàng)建NSInvocationOperation對象
  NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
  //2.開始執(zhí)行
 [operation start];

2.NSBlockOperation
  //1.創(chuàng)建NSBlockOperation對象
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@", [NSThread currentThread]);
  }];
  //2.開始任務(wù)
 [operation start];
NSBlockOperation還有一個方法:addExecutionBlock: 通過這個方法可以給Operation添加多個執(zhí)行Block。
這樣Operation中的任務(wù)會并發(fā)執(zhí)行郑原,它會在主線程和其它的多個線程執(zhí)行這些任務(wù)


創(chuàng)建隊列:
只要添加到隊列唉韭,會自動調(diào)用任務(wù)的 start() 方法

1.主隊列
NSOperationQueue *queue = [NSOperationQueue mainQueue];

2.其他隊列
//1.創(chuàng)建一個其他隊列    
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.創(chuàng)建NSBlockOperation對象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多個Block
for (NSInteger i = 0; i < 5; i++) {
    [operation addExecutionBlock:^{
        NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
    }];
}
//4.隊列添加任務(wù)
[queue addOperation:operation];

將NSOperationQueue與GCD的隊列相比較就會發(fā)現(xiàn),這里沒有串行隊列犯犁,那如果我想要10個任務(wù)在其他線程串行的執(zhí)行怎么辦属愤?
這就是蘋果封裝的妙處,你不用管串行酸役、并行住诸、同步、異步這些名詞簇捍。NSOperationQueue有一個參數(shù)maxConcurrentOperationCount最大并發(fā)數(shù),用來設(shè)置最多可以讓多少個任務(wù)同時執(zhí)行俏拱。當你把它設(shè)置為1的時候暑塑,他不就是串行了嘛!
NSOperation 有一個非常實用的功能锅必,那就是添加依賴事格。比如有 3 個任務(wù):A: 從服務(wù)器上下載一張圖片惕艳,B:給這張圖片加個水印,C:把圖片返回給服務(wù)器驹愚。這時就可以用到依賴了

  • 14.2什么時候使用GCD远搪,什么時候使用NSOperation?如何按照指定的執(zhí)行順序完成任務(wù)逢捺?例如谁鳍,c任務(wù)需要在a、b任務(wù)完成之后執(zhí)行劫瞳,請分別使用GCD和NSOperation寫書代碼示例
  • 14.3 有個圖片下載未完成劃過后重新出發(fā)沒下載圖片會怎樣
  • 14.4 GCD的隊列(dispatch_queue_t)分哪兩種類型倘潜?

串行隊列和并行隊列

  • 14.5 如何用GCD同步若干個異步調(diào)用?(如根據(jù)若干個url異步加載多張圖片志于,然后在都下載完成后合成一張整圖)

總體上說: 使用 dispatch group涮因,然后 wait forever 等待完成, 或者采取 group notify 來通知回調(diào)伺绽。
細節(jié):
1. 創(chuàng)建異步隊列
2. 創(chuàng)建dispatch_group dispatch_group_t = dispatch_group_create()
3. 通過組來執(zhí)行異步下載任務(wù) dispatch_group_async(queueGroup, aQueue, ^{
NSLog(@"下載圖片."); });
4.等到所有任務(wù)完成 dispatch_group_wait(queueGroup, DISPATCH_TIME_FOREVER);
5.合成圖片

  使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢养泡,就會執(zhí)行Main Dispatch Queue中的結(jié)束處理的block。

  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, ^{ /*加載圖片1 */ });
  dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
  dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); 
  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 合并圖片
  });

######15. ARC內(nèi)存管理的理解
《Effective Objective-C 2.0》
理解引用計數(shù)
- 引用計數(shù)工作原理
- 屬性存取方法中的內(nèi)存管理
- 自動釋放池
- 保留環(huán)

以arc簡化引用計數(shù)
- 使用ARC時必須遵循的方法命名規(guī)則
- 變量的內(nèi)存管理語義
- arc如何清理實例變量
- 覆寫內(nèi)存管理方法
- 用“僵尸對象”調(diào)試內(nèi)存管理問題
- 不要使用retain count

在dealloc方法中只釋放引用并解除監(jiān)聽
編寫"異常安全代碼"時注意內(nèi)存管理問題
以弱引用避免保留環(huán)
以"自動釋放池塊"降低內(nèi)存峰值

《Objective-C高級編程》
>Apple 在Objective-C中采用ARC機制奈应,讓編譯器來進行內(nèi)存管理澜掩。在新一代Apple LLVM編譯器中設(shè)置ARC為有效狀態(tài),就無需再次鍵入retain或release代碼钥组,這在降低程序崩潰输硝,內(nèi)存泄漏等風險的同時,很大程度上減少了開發(fā)程序的工作量程梦。編譯器完全清楚目標對象点把,并能立刻釋放那些不再被使用的對象。如此依賴屿附,應(yīng)用程序?qū)⒕哂锌深A測性郎逃,且能流程運行,速度也將大幅提升挺份。

- 自己生成的對象褒翰,自己持有
- 非自己生成的對象,自己也能持有
- 不再需要自己持有的對象時釋放
- 非自己持有的對象無法釋放


- ######15.1.內(nèi)存泄漏有哪些情況
   - 循環(huán)引用
      - Delegate
我們在使用代理設(shè)計模式的時候匀泊,一定要注意將 delegate 變量聲明為 weak 類型优训,像這樣 @property (nonatomic, weak) id delegate;
      - Block
__weak typeof(self)weakSelf = self;
      - NSTimer
在一個ViewController里創(chuàng)建了一個定時器,并且repeats:值為YES各聘,一定記得在 pop/dismiss當前ViewController將timer設(shè)為invalidate揣非,否則會造成循環(huán)引用,這里要特別需要注意的一點是:我們不要在ViewController的dealloc方法里調(diào)用[timer invalidate]; 因為從來不會調(diào)到這里躲因,我們應(yīng)該在viewWillDisappear里邊調(diào)用

   - performSelector延時調(diào)用導致的內(nèi)存泄露
假設(shè)你延時10s觸發(fā)某個方法早敬,但是在3s的時候忌傻,用戶點擊了back,這時對象不能馬上被回收搞监,而是要等到10s后才有可能被回收水孩。所以在項目中如果要延時觸發(fā)方法,我不會選擇該方法琐驴,而是使用GCD
         __weak typeof(self) weakSelf = self;
         double delayInSeconds = 10.0;
         dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds *NSEC_PER_SEC);
         dispatch_after(popTime, dispatch_get_main_queue(), ^{
             [weakSelf dodd];
         });

   - 代理未清空引起野指針
iOS的一些API俘种,發(fā)現(xiàn)delegate都是assign的,這樣就會引起野指針的問題棍矛,可能會引起一些莫名其妙的crash安疗。那么這是怎么引起的,當一個對象被回收時够委,對應(yīng)的delegate實體也就被回收荐类,但是delegate的指針確沒有被nil,從而就變成了游蕩的野指針了茁帽。所以在delloc方法中要將對應(yīng)的assign代理設(shè)置為nil
一般自己寫的一些delegate玉罐,我們會用weak,而不是assign潘拨,weak的好處是當對應(yīng)的對象被回收時吊输,指針也會自動被設(shè)置為nil。

- ######15.2 什么時候用@autoreleasepool
   - 寫基于命令行的的程序時铁追,就是沒有UI框架季蚂,如AppKit等Cocoa框架時。
   - 寫循環(huán)琅束,循環(huán)里面包含了大量臨時創(chuàng)建的對象扭屁。(500000次循環(huán),每次循環(huán)創(chuàng)建一個NSNumber實例和兩個NSString實例)
   - 創(chuàng)建了新的線程涩禀。(非Cocoa程序創(chuàng)建線程時才需要)
   - 長時間在后臺運行的任務(wù)料滥。

- ######15.3. 頁面使用過多內(nèi)存過多閃退
- ######15.4. 1000張圖片內(nèi)存
- ######15.5.界面交互優(yōu)化騰訊 banner100頁怎么辦

######16. 詳細描述一下KVO的實現(xiàn)機制,代理 通知和kvo區(qū)別 代理和block區(qū)別 block 訪問外部變量原理


######17. runtime的理解
- runtime作用
     - 發(fā)送消息
消息機制原理:對象根據(jù)方法編號SEL去映射表查找對應(yīng)的方法實現(xiàn)
     - 交換方法
開發(fā)使用場景:系統(tǒng)自帶的方法功能不夠艾船,給系統(tǒng)自帶的方法擴展一些功能葵腹,并且保持原有的功能。
方式一:繼承系統(tǒng)的類屿岂,重寫方法.
方式二:使用runtime,交換方法.
           // 獲取imageWithName方法地址
           Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
           // 獲取imageWithName方法地址
           Method imageName = class_getClassMethod(self, @selector(imageNamed:));
           // 交換方法地址践宴,相當于交換實現(xiàn)方式
           method_exchangeImplementations(imageWithName, imageName);
     - 動態(tài)添加方法
開發(fā)使用場景:如果一個類方法非常多,加載類到內(nèi)存的時候也比較耗費資源爷怀,需要給每個方法生成映射表阻肩,可以使用動態(tài)給某個類,添加方法解決霉撵。
經(jīng)典面試題:有沒有使用performSelector磺浙,其實主要想問你有沒有動態(tài)添加過方法。
簡單使用
           @implementation Person
           // void(*)()
           // 默認方法都有兩個隱式參數(shù)徒坡,
           void eat(id self,SEL sel)
           {
               NSLog(@"%@ %@",self,NSStringFromSelector(sel));
           }
           // 當一個對象調(diào)用未實現(xiàn)的方法撕氧,會調(diào)用這個方法處理,并且會把對應(yīng)的方法列表傳過來.
           // 剛好可以用來判斷,未實現(xiàn)的方法是不是我們想要動態(tài)添加的方法
           + (BOOL)resolveInstanceMethod:(SEL)sel
           {
               if (sel == @selector(eat)) {
                   // 動態(tài)添加eat方法
                   // 第一個參數(shù):給哪個類添加方法
                   // 第二個參數(shù):添加方法的方法編號
                   // 第三個參數(shù):添加方法的函數(shù)實現(xiàn)(函數(shù)地址)
                   // 第四個參數(shù):函數(shù)的類型喇完,(返回值+參數(shù)類型) v:void @:對象->self :表示SEL->_cmd
                   class_addMethod(self, @selector(eat), eat, "v@:");
               }
               return [super resolveInstanceMethod:sel];
           }
           @end
     - 給分類添加屬性
原理:給一個類聲明屬性伦泥,其實本質(zhì)就是給這個類添加關(guān)聯(lián),并不是直接把這個值的內(nèi)存空間添加到類存空間锦溪。
           static const char *key = "name";
           @implementation NSObject (Property)
           - (NSString *)name
           {
               // 根據(jù)關(guān)聯(lián)的key不脯,獲取關(guān)聯(lián)的值。
               return objc_getAssociatedObject(self, key);
           }
           - (void)setName:(NSString *)name
           {
               // 第一個參數(shù):給哪個對象添加關(guān)聯(lián)
               // 第二個參數(shù):關(guān)聯(lián)的key刻诊,通過這個key獲取
                // 第三個參數(shù):關(guān)聯(lián)的value
               // 第四個參數(shù):關(guān)聯(lián)的策略
               objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
           }
           @end
     - 字典轉(zhuǎn)模型
- runtime如何通過selector找到對應(yīng)的IMP地址防楷?(分別考慮類方法和實例方法)
每一個類對象中都一個方法列表,方法列表中記錄著方法的名稱,方法實現(xiàn),以及參數(shù)類型,其實selector本質(zhì)就是方法名稱,通過這個方法名稱就可以在方法列表中找到對應(yīng)的方法實現(xiàn).
- category中能不能使用聲明屬性?為什么则涯?如果能复局,怎么實現(xiàn)?
給分類(Category)添加屬性利用Runtime實現(xiàn)getter/setter 方法

@interface ClassName (CategoryName)
@property (nonatomic, strong) NSString *str;
@end

//實現(xiàn)文件

import "ClassName + CategoryName.h"

import <objc/runtime.h>

static void *strKey = &strKey;
@implementation ClassName (CategoryName)

-(void)setStr:(NSString *)str
{
objc_setAssociatedObject(self, & strKey, str, OBJC_ASSOCIATION_COPY);
}

-(NSString *)str
{
return objc_getAssociatedObject(self, &strKey);
}

@end

- 什么時候會報unrecognized selector的異常粟判?
簡單來說:當該對象上某個方法,而該對象上沒有實現(xiàn)這個方法的時候亿昏, 可以通過“消息轉(zhuǎn)發(fā)”進行解決。
簡單的流程如下:objc是動態(tài)語言档礁,每個方法在運行時會被動態(tài)轉(zhuǎn)為消息發(fā)送角钩,即:objc_msgSend(receiver, selector)。
objc在向一個對象發(fā)送消息時呻澜,runtime庫會根據(jù)對象的isa指針找到該對象實際所屬的類递礼,然后在該類中的方法列表以及其父類方法列表中尋找方法運行,如果易迹,在最頂層的父類中依然找不到相應(yīng)的方法時宰衙,程序在運行時會掛掉并拋出異常unrecognized selector sent to XXX 。但是在這之前睹欲,objc的運行時會給出三次拯救程序崩潰的機會:
`Method resolution`
objc運行時會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:供炼,讓你有機會提供一個函數(shù)實現(xiàn)。如果你添加了函數(shù)并返回 YES窘疮,那運行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程袋哼,如果 resolve 方法返回 NO ,運行時就會移到下一步闸衫,消息轉(zhuǎn)發(fā)(Message Forwarding)涛贯。
`Fast forwarding`
如果目標對象實現(xiàn)了-forwardingTargetForSelector:,Runtime 這時就會調(diào)用這個方法蔚出,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機會弟翘。 只要這個方法返回的不是nil和self虫腋,整個消息發(fā)送的過程就會被重啟,當然發(fā)送的對象會變成你返回的那個對象稀余。否則悦冀,就會繼續(xù)Normal Fowarding。 這里叫Fast睛琳,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機制盒蟆。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個NSInvocation對象师骗,所以相對更快點历等。
`Normal forwarding`
這一步是Runtime最后一次給你挽救的機會。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型辟癌。如果-methodSignatureForSelector:返回nil寒屯,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了黍少。如果返回了一個函數(shù)簽名浩螺,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標對象。


######18. runloop的理解
- RunLoop 的概念
如果我們需要一個機制仍侥,讓線程能隨時處理事件但并不退出要出,這種模型通常被稱作 Event Loop。Event Loop 在很多系統(tǒng)和框架里都有實現(xiàn)农渊,比如 OSX/iOS 里的 RunLoop患蹂。實現(xiàn)這種模型的關(guān)鍵點在于:如何管理事件/消息,如何讓線程在沒有處理消息時休眠以避免資源占用砸紊、在有消息到來時立刻被喚醒传于。
- RunLoop 與線程的關(guān)系
蘋果不允許直接創(chuàng)建 RunLoop,它只提供了兩個自動獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()醉顽。
線程和 RunLoop 之間是一一對應(yīng)的沼溜,其關(guān)系是保存在一個全局的 Dictionary 里。線程剛創(chuàng)建時并沒有 RunLoop游添,如果你不主動獲取系草,那它一直都不會有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時唆涝,RunLoop 的銷毀是發(fā)生在線程結(jié)束時找都。你只能在一個線程的內(nèi)部獲取其 RunLoop(主線程除外)。




######19. contentoffset contentinset contentsize
######20. 說說知道哪些設(shè)計模式廊酣,說說簡單工廠和抽象工廠
######21.第三方分享的調(diào)用接口
######22.動畫的實現(xiàn)方式
######23. 錯誤提示sympol
######24. 使用定時器注意問題
######25. 頁面滑動丟幀的原因有多少種
######26.推送的原理具體實現(xiàn)
######27. 如果不用第三方庫實現(xiàn)圖片緩存設(shè)計一種方法
######28. 獲得App閃退log怎么做
######29. 完整發(fā)布流程

- 如何重寫帶 copy 關(guān)鍵字的 setter能耻?
重寫copy的setter方法時候,一定要調(diào)用一下傳入的對象的copy方法,然后在賦值給該setter的方法對應(yīng)的成員變量

- @synthesize和@dynamic分別有什么作用?
   - @property有兩個對應(yīng)的詞,一個是@synthesize晓猛,一個是@dynamic饿幅。如果@synthesize和@dynamic都沒寫,那么默認的就是@syntheszie var = _var;
   - @synthesize的語義是如果你沒有手動實現(xiàn)setter方法和getter方法戒职,那么編譯器會自動為你加上這兩個方法诫睬。
   - @dynamic告訴編譯器,屬性的setter與getter方法由用戶自己實現(xiàn),不自動生成帕涌。(當然對于readonly的屬性只需提供getter即可)。假如一個屬性被聲明為@dynamic var续徽,然后你沒有提供@setter方法和@getter方法蚓曼,編譯的時候沒問題,但是當程序運行到instance.var =someVar钦扭,由于缺setter方法會導致程序崩潰纫版;或者當運行到 someVar = var時,由于缺getter方法同樣會導致崩潰客情。編譯時沒問題其弊,運行時才執(zhí)行相應(yīng)的方法,這就是所謂的動態(tài)綁定膀斋。
- BAD_ACCESS在什么情況下出現(xiàn)梭伐,如何調(diào)試?
   1.  死循環(huán)了
   2.  訪問一個僵尸對象
設(shè)置全局斷點快速定位問題代碼所在行

- 談?wù)刬nstancetype和id的異同
1仰担、相同點
都可以作為方法的返回類型
2糊识、不同點
①instancetype可以返回和方法所在類相同類型的對象,id只能返回未知類型的對象摔蓝;②instancetype只能作為返回值赂苗,不能像id那樣作為參數(shù)

- @protocol 和 category 中如何使用 @property
1)在protocol中使用property只會生成setter和getter方法聲明,我們使用屬性的目的,是希望遵守我協(xié)議的對象能實現(xiàn)該屬性
2)category 使用 @property 也是只會生成setter和getter方法的聲明,如果我們真的需要給category增加屬性的實現(xiàn),需要借助于運行時的兩個函數(shù):
①objc_setAssociatedObject
②objc_getAssociatedObject



最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市贮尉,隨后出現(xiàn)的幾起案子拌滋,更是在濱河造成了極大的恐慌,老刑警劉巖猜谚,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件败砂,死亡現(xiàn)場離奇詭異,居然都是意外死亡魏铅,警方通過查閱死者的電腦和手機吠卷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沦零,“玉大人祭隔,你說我怎么就攤上這事。” “怎么了疾渴?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵千贯,是天一觀的道長。 經(jīng)常有香客問我搞坝,道長搔谴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任桩撮,我火速辦了婚禮敦第,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘店量。我一直安慰自己芜果,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布融师。 她就那樣靜靜地躺著右钾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旱爆。 梳的紋絲不亂的頭發(fā)上舀射,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音怀伦,去河邊找鬼脆烟。 笑死,一個胖子當著我的面吹牛房待,可吹牛的內(nèi)容都是我干的浩淘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼吴攒,長吁一口氣:“原來是場噩夢啊……” “哼张抄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洼怔,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤署惯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后镣隶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體极谊,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年安岂,在試婚紗的時候發(fā)現(xiàn)自己被綠了轻猖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡域那,死狀恐怖咙边,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤败许,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布王带,位于F島的核電站,受9級特大地震影響市殷,放射性物質(zhì)發(fā)生泄漏愕撰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一醋寝、第九天 我趴在偏房一處隱蔽的房頂上張望搞挣。 院中可真熱鬧,春花似錦音羞、人聲如沸囱桨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至婶肩,卻和暖如春办陷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背律歼。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工民镜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人险毁。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓制圈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親畔况。 傳聞我的和親對象是個殘疾皇子鲸鹦,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,125評論 29 470
  • 多線程、特別是NSOperation 和 GCD 的內(nèi)部原理跷跪。運行時機制的原理和運用場景馋嗜。SDWebImage的原...
    LZM輪回閱讀 2,004評論 0 12
  • ———————————————回答好下面的足夠了---------------------------------...
    恒愛DE問候閱讀 1,712評論 0 4
  • iOS面試小貼士 ———————————————回答好下面的足夠了------------------------...
    不言不愛閱讀 1,962評論 0 7
  • 重拾《生活大爆炸》 謝爾頓在霍華德的婚禮上致辭,他說:人窮盡一生去追尋另一個人類的故事吵瞻,我一直都無法理解葛菇,或許我...
    譚聞樂見閱讀 500評論 0 0