iOS基礎相關

1. 什么是ARC?

引用計數(shù)機制
在 Objective-C 中,利用 引用計數(shù)器 來進行內存管理:每個對象都有一個對應的引用計數(shù),當這個對象被持有的時,其引用計數(shù)就會遞增,當這個對象的某個持有被釋放時,對象的引用計數(shù)就會遞減,當這個對象的引用計數(shù)為 0 的時候,這個對象就會被釋放。

每個對象都對應著一個引用計數(shù),在內存中,通過一個 SideTable RefcountMap 來存儲這個對應關系:對象的地址作為 Key,引用計數(shù)的值作為 Value。

struct SideTable {
    // 保證原子操作的自旋鎖
    spinlock_t slock;
    // 引用計數(shù)的 hash 表
    RefcountMap refcnts;
    // weak 引用全局 hash 表
    weak_table_t weak_table;
}

struct weak_table_t {
    // 保存了所有指向指定對象的 weak 指針
    weak_entry_t *weak_entries;
    // 存儲空間
    size_t    num_entries;
    // 參與判斷引用計數(shù)輔助量
    uintptr_t mask;
    // hash key 最大偏移值
    uintptr_t max_hash_displacement;
};

在 iOS 中,Objective-C中提供了 兩種機制來管理對象的引用計數(shù)器,第一種是MRC(內存的手動管理),第二種是ARC(自動管理內存)

  • MRC: 需要手動添加的用來處理內存管理的引用計數(shù)的代碼,與對變量的管理相關的方法有:retain,release和autorelease。retain和release方法操作的是引用計數(shù),當引用計數(shù)為零時,便自動釋放內存
  • 在ARC的內存管理中,都是由系統(tǒng)去管理的,編譯器自動幫我們添加代碼,不需要我們去做任何內存操作。
2. block一般用那個關鍵字修飾,為什么?

copy修飾,把MRC下的棧block拷貝到堆里,防止訪問的時候block銷毀,造成崩潰

3. 用@property聲明的NSString(或NSArray,NSDictionary)經常使用copy關鍵字,為什么?如果改用strong關鍵字,可能造成什么問題?
  • 用@property聲明 NSString、NSArray、NSDictionary 經常使用copy關鍵字,是因為他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,他們之間可能進行賦值操作,為確保對象中的字符串值不會無意間變動,應該在設置新屬性值時拷貝一份。
  • 如果我們使用是strong,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性。

如果我們使用是strong,那么這個屬性就有可能指向一個可變對象,如果這個可變對象在外部被修改了,那么會影響該屬性。

5. @property 的本質是什么?ivar、getter、setter 是如何生成并添加到這個類中的。

“屬性”(property)有兩大概念:ivar(實例變量)、存取方法(access method=getter),即@property = ivar + getter + setter

Xcode5.0 之后編譯器默認幫我們實現(xiàn)了這些

6. 分別寫一個setter方法用于完成

@property (nonatomic,retain)NSString *name
@property (nonatomic,copy) NSString *name

-(void)setName:(NSString *)name
{
    [name retain];
    [_name release];
    _name = name;
}
-(void)setName:(NSString *)name
{
    
    [_name release];
    _name = [name copy];
}
7. 說說assign vs weak,_block vs _weak的區(qū)別
8. 請說出下面代碼是否有問題,如果有問題請修改?
@autoreleasepool {
        for (int i=0; i[largeNumber; i++) { (因識別問題,該行代碼中尖括號改為方括號代替)
            Person *per = [[Person alloc] init];
            [per autorelease];
        }
    }

autorelease雖然會使引用計數(shù)減一,但是它并不是立即減一,它的本質功能只是把對象放到離他最近的自動釋放池里。當自動釋放池銷毀了,才會向自動釋放池中的每一個對象發(fā)送release消息。這道題的問題就在autorelease。因為largeNumber是一個很大的數(shù),autorelease又不能使引用計數(shù)立即減一,所以在循環(huán)結束前會造成大次數(shù)循環(huán)內存暴漲溢出。

@autoreleasepool {
        for (int i=0; i[100000; i++) { (因識別問題,該行代碼中尖括號改為方括號代替)
            @autoreleasepool {
            Person *per = [[Person alloc] init];
            [per autorelease];
        }
      }
    }
9. 請問下面代碼是否有問題,如有問題請修改?
@autoreleasepool {
        NSString *str = [[NSString alloc] init];
        [str retain];
        [str retain];
        str = @"jxl";
        [str release];
        [str release];
        [str release];
}

這道題跟上題一樣存在內存泄露問題
1.內存泄露
2.指向常量區(qū)的對象不能release。

指針變量str原本指向一塊開辟的堆區(qū)空間,但是經過重新給str賦值,str的指向發(fā)生了變化,由原來指向堆區(qū)空間,到指向常量區(qū)。常量區(qū)的變量根本不需要釋放,這就導致了原來開辟的堆區(qū)空間沒有釋放,照成內存泄露。

10. 什么情況下使用weak關鍵字,相比assign有什么不同?什么情況使用weak關鍵字?
什么情況使用 weak 關鍵字?
  1. 在 ARC 中,在有可能出現(xiàn)循環(huán)引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate、block。
  2. 自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義IBOutlet 控件屬性一般也使用 weak,使用 storyboard(xib 不行)創(chuàng)建的 vc,會有一個叫 _topLevelObjectsToKeepAliveFromStoryboard 的私有數(shù)組強引用所有 top level 的對象,所以這時即便 outlet 聲明成 weak 也沒關系。當然,也可以使用 strong。
weak 和 assign 的不同點:
  1. weak、assign 修飾的屬性指向一個對象時都不會增加對象的引用計數(shù)。然而在所指的對象被釋放時,weak 屬性值會被置為 nil,而assign 屬性不會。
  2. assign 可以用非 OC 對象以及基本類型,而 weak 必須用于 OC 對象。
11.內存管理語義(assign、strong、weak等的區(qū)別)
  • assign
    主要用于修飾基本數(shù)據(jù)類型,例如NSInteger,CGFloat,存儲在棧中,內存不用程序員管理。assign是可以修飾對象的,但是會出現(xiàn)野指針問題。
  • weak
    weak 修飾符指向但是并不持有該對象(弱引用),引用計數(shù)也不會加1。在 Runtime 中對該屬性進行了相關操作,無需處理,可以自動銷毀。weak用來修飾對象,多用于避免循環(huán)引用的地方。weak 不可以修飾基本數(shù)據(jù)類型
  • unsafe_unretained
    此特質的語義和assign相同,但是它適用于“對象類型”,該特質表達一種“非擁有關系”,當目標對象遭到推毀時,屬性值不會自動清空,這一點與weak有區(qū)別。
  • copy
    copy關鍵字和 strong類似,copy 多用于修飾有可變類型的不可變對象上 NSString,NSArray,NSDictionary上
  • Strong
    Strong 修飾符表示指向并持有該對象(強引用),其修飾對象的引用計數(shù)會加1。該對象只要引用計數(shù)不為0就不會被銷毀。當然可以通過將變量強制賦值 nil 來進行銷毀。
  • retain
    retain屬性的setter方法是保留新值并釋放舊值,然后更新實例變量,令其指向新值。

參考

12. @synthesize和@dynamic分別有什么作用?
  • @property 有兩個對應的詞,一個是 @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í)行相應的方法,這就是所謂的動態(tài)綁定。
13. @property中有哪些屬性關鍵字?

屬性可以擁有的特質分為四類:

  1. 原子性--- nonatomic 特質
    在默認情況下,由編譯器合成的方法會通過鎖定機制確保其原子性(atomicity)。如果屬性具備 nonatomic 特質,則不使用自旋鎖。請注意,盡管沒有名為“atomic”的特質(如果某屬性不具備 nonatomic 特質,那它就是“原子的” ( atomic) ),但是仍然可以在屬性特質中寫明這一點,編譯器不會報錯。若是自己定義存取方法,那么就應該遵從與屬性特質相符的原子性。
  2. 讀/寫權限---readwrite(讀寫)、readonly (只讀)
  3. 內存管理語義---assign、strong、 weak、unsafe_unretained、copy
  4. 方法名---getter=<name> 、setter=<name>
14. ARC下,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
  • 對應基本數(shù)據(jù)類型默認關鍵字是
    atomic, readwrite, assign
  • 對于普通的 Objective-C 對象
    atomic, readwrite, strong
15. 如何讓自己的類用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter?

若想令自己所寫的對象具有拷貝功能,則需實現(xiàn) NSCopying 協(xié)議。如果自定義的對象分為可變版本與不可變版本,那么就要同時實現(xiàn) NSCopyingNSMutableCopying 協(xié)議。

具體步驟:

  1. 需聲明該類遵從 NSCopying 協(xié)議
  2. 實現(xiàn) NSCopying 協(xié)議。該協(xié)議只有一個方法:
- (id)copyWithZone:(NSZone *)zone;

對于很多現(xiàn)有類,如NSString,NSDictionary,。。。這個方法已經實現(xiàn)
至于如何重寫帶 copy 關鍵字的 setter這個問題,

如果拋開本例來回答的話,如下:

- (void)setName:(NSString *)name {
    //[_name release]; MRC
    _name = [name copy];
}
15. 如何調試BAD_ACCESS錯誤

野指針:指針指向的對象已經被回收掉了.這個指針就叫做野指針

僵尸對象 : 一個OC對象引用計數(shù)為0被釋放后就變成僵尸對象了,僵尸對象的內存已經被系統(tǒng)回收,雖然可能該對象還存在,數(shù)據(jù)依然在內存中,但僵尸對象已經是不穩(wěn)定對象了,不可以再訪問或者使用,它的內存是隨時可能被別的對象申請而占用

BAD_ACCESS:野指針錯誤,主要的原因是,當某個對象被完全釋放,也就是retainCount引用計數(shù)為0后。再去通過該對象去調用release或者訪問成員變量就會發(fā)生野指針錯誤

  1. 重寫object的respondsToSelector方法,現(xiàn)實出現(xiàn)EXEC_BAD_ACCESS前訪問的最后一個object
  2. 通過Zombie
  3. 設置全局斷點快速定位問題代碼所在行
  4. Xcode 7 已經集成了BAD_ACCESS捕獲功能:Address Sanitizer。 用法如下:在配置中勾選?Enable Address Sanitizer


    image.png
使用野指針訪問僵尸對象.有的時候會出問題報錯(EXC_BAD_ACCESS),有的時候不會出問題
  • 當野指針指向的僵尸對象所占用的空間還沒有分配給別人的時候,這個時候其實是可以訪問的.因為對象的數(shù)據(jù)還在.
  • 當野指針指向的對象所占用的空間分配給了別人的時候 這個時候訪問就會出問題. 所以,你不要通過1個野指針去訪問1個僵尸對象.
16. iOS nil,Nil,NULL,NSNULL的區(qū)別

nil      (id)0
是OC對象的空指針,可正常調用方法(返回空值,false,零值等)

Nil     (Class)0
是OC類的空指針,主要運用于runtime中,Class c = Nil; 其他特性與nil一致

NULL     (void *)0
是C指針的空值,在OC中對非對象指針賦空值,如C指針,int *p = NULL

NSNULL    [NSNULL null]
是OC中的空對象,可補足NSArray,NSDictinory中不能存儲nil的缺陷,在命令行輸出一般為"null"

17. 反射機制

內?。?code>反射)機制是面向對象語言的一個強大特性 , 檢查對象自己在運行時的信息(在繼承樹上的位置,是否遵循特定的協(xié)議,是否可以響應特定的消息)來避免出現(xiàn)未識別方法等問題。
1、獲取Class對象

// 獲取Class對象
  + (Class)class; 

Class對象其實本質上就是一個結構體,這個結構體中的成員變量還是自己,這種設計方式非常像鏈表的數(shù)據(jù)結構。
  typedef struct objc_class *Class;
  struct objc_class {
      Class isa  OBJC_ISA_AVAILABILITY;                                  
  }

//實例對象獲取Class對象
  [self class]
//類對象獲取Class對象
  [Person class]        

2、動態(tài)的調用方法

// SEL和字符串轉換
FOUNDATION_EXPORT NSString *NSStringFromSelector(SEL aSelector);
FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);
// Class和字符串轉換
FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);
FOUNDATION_EXPORT Class __nullable NSClassFromString(NSString *aClassName);
// Protocol和字符串轉換
FOUNDATION_EXPORT NSString *NSStringFromProtocol(Protocol *proto) NS_AVAILABLE(10_5, 2_0);
FOUNDATION_EXPORT Protocol * __nullable NSProtocolFromString(NSString *namestr) NS_AVAILABLE(10_5, 2_0);

通過這些方法,我們可以在運行時選擇創(chuàng)建那個實例,并動態(tài)選擇調用哪個方法。

3、檢查繼承關系

// 當前對象是否這個類或其子類的實例
    - (BOOL)isKindOfClass:(Class)aClass;    
// 當前對象是否是這個類的實例  
    - (BOOL)isMemberOfClass:(Class)aClass;  

// 當前對象是否遵守這個協(xié)議
  - (BOOL)conformsToProtocol:(Protocol *)aProtocol;
// 當前對象是否實現(xiàn)這個方法
  - (BOOL)respondsToSelector:(SEL)aSelector;
18. 一個NSObject對象占多大的內存?
image.png
image.png

所以我們可以很好的回答這個問題,系統(tǒng)分配了16個字節(jié)空間給NSObject對象,但是在64位環(huán)境下,NSObject只使用了8個字節(jié)

19. 你知道有哪些情況會導致app崩潰,分別可以用什么方法攔截并化解?
  • unrecognized selector crash

可以利用Runtime的消息轉發(fā)機制,通過重寫NSObject的forwardingTargetForSelector方法,我們就可以將無法識別的方法進行攔截并且將消息轉發(fā)到安全的樁類對象

  • KVO crash

參考KVOViewCOntroller,創(chuàng)建一個中間代理對象,被觀察對象dealloc之前,可以通過delegate自動將與自己有關的KVO關系都注銷掉,避免了KVO的被觀察者dealloc時仍然注冊著KVO導致的crash

  • NSNotification crash

NSNotification Crash的防護原理很簡單, 利用method swizzling hook NSObject的dealloc函數(shù),再對象真正dealloc之前先調用一下removeObserver:即可。
同時hookNSNotificationCenteraddObserver函數(shù),在其添加observer的時候,對observer動態(tài)添加標記flag。這樣在observer dealloc的時候,就可以通過flag標記來判斷其是否有必要調用removeObserver函數(shù)了。

  • NSTimer的crash

參考NSTimer防止循環(huán)應用的方法

  • Container crash(數(shù)組越界NSRangeException,插nil等)
  • NSString crash (字符串操作的crash)

NSArray/NSMutableArray/NSDictionary/NSMutableDictionary/NSCache的一些常用的會導致崩潰的API進行method swizzling,然后在swizzle的新方法中加入一些條件限制和判斷,從而讓這些API變的安全,比如AvoidCrash就是給各個系統(tǒng)類的添加分類實現(xiàn)method swizzling

  • UI not on Main Thread Crash (非主線程刷UI)
  • 野指針、僵尸對象

AvoidCrash
參考文章

20. [self class] 與 [super class]

下面代碼輸出什么?

 @implementation Son : Father
- (id)init
{
    self = [super init];
    if (self)
    {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
return self;
}
@end

self和super的區(qū)別:

self 是類的一個隱藏參數(shù),每個方法的實現(xiàn)的第一個參數(shù)即為self。
super 并不是隱藏參數(shù),它實際上只是一個”編譯器標示符”,它負責告訴編譯器,當調用方法時,去調用父類的方法,而不是本類中的方法。

在調用[super class]的時候,runtime會去調用objc_msgSendSuper方法,而不是objc_msgSend

 id objc_msgSendSuper(struct objc_super *super, SEL op, ...)
 
 struct objc_super {
     id receiver;
     Class cls; // the class to search
  }

在objc_msgSendSuper方法中,第一個參數(shù)是一個 objc_super 的結構體,這個結構體里面有兩個變量,一個是接收消息的 receiver,一個是 當前類的父類 super_class

objc_msgSendSuper 的工作原理:
從objc_super結構體指向的superClass父類的方法列表開始查找selector,找到后以objc->receiver去調用這個selector。注意,最后的調用者是objc->receiver

objc_super->receiver = self
21. isKindOfClass 與 isMemberOfClass

下面代碼輸出什么?

 @interface Sark : NSObject
 @end

 @implementation Sark
 @end

 int main(int argc, const char * argv[]) {
@autoreleasepool {
    BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL res3 = [(id)[Sark class] isKindOfClass:[Sark class]];
    BOOL res4 = [(id)[Sark class] isMemberOfClass:[Sark class]];

   NSLog(@"%d %d %d %d", res1, res2, res3, res4);
}
return 0;
}

先來分析一下源碼

+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

inline Class 
objc_object::getIsa() 
{
    if (isTaggedPointer()) {
        uintptr_t slot = ((uintptr_t)this >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
        return objc_tag_classes[slot];
    }
    return ISA();
}

inline Class 
objc_object::ISA() 
{
    assert(!isTaggedPointer()); 
    return (Class)(isa.bits & ISA_MASK);
}

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
        if (tcls == cls) return YES;
    }
    return NO;
}

+ (BOOL)isMemberOfClass:(Class)cls {
    return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
image.png

第一行res1輸出應該為YES
第二行res2輸出NO
第三行的res3輸出為NO
第四行res4輸出NO

Root class(meta) 的 superclass 就是 Root class(class),也就是NSObject本身。所以第二次循環(huán)相等,于是第一行res1輸出應該為YES。

22. Objective-C 如何實現(xiàn)多重繼承?

Object-c的類沒有多繼承,只支持單繼承,如果要實現(xiàn)多繼承的話,可使用如下幾種方式間接實現(xiàn)
1. 通過組合實現(xiàn)
A和B組合,作為C類的組件
2. 通過協(xié)議實現(xiàn)
C類實現(xiàn)A和B類的協(xié)議方法
3. 消息轉發(fā)實現(xiàn)
forwardInvocation:方法

23. LLDB常用的調試命令有哪些?

po:print object的縮寫,表示顯示對象的文本描述,如果對象不存在則打印nil。
p:可以用來打印基本數(shù)據(jù)類型。
call:執(zhí)行一段代碼 如:call NSLog(@"%@", @"yang")
bt:打印當前線程堆棧信息 (bt all打印所有線程堆棧信息)
expr:動態(tài)執(zhí)行指定表達式
image:常用來尋找棧地址對應代碼位置 如:image lookup --address 0xxxx
breakpoint:斷點操作

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容