category 和 extension 的區別
- 分類有名字,類擴展沒有分類名字,是一種特殊的分類
- 分類只能擴展方法(屬性僅僅是聲明,並沒真正實現),類擴展能夠擴展屬性、成員變量和方法
define 和 const常量有什麼區別?
- define在預處理階段進行替換,const常量在編譯階段使用
- 宏不作類型檢查,僅僅進行替換,const常量有數據類型,會執行類型檢查
- define不能調試,const常量能夠調試
- define定義的常量在替換後運行過程當中會不斷地佔用內存,而const定義的常量存儲在數據段只有一份copy,效率更高
- define能夠定義一些簡單的函數,const不能夠
block和weak修飾符的區別?
- __block無論是ARC仍是MRC模式下均可以使用,能夠修飾對象,也能夠修飾基本數據類型
- __weak只能在ARC模式下使用,只能修飾對象(NSString),不能修飾基本數據類型
- block修飾的對象能夠在block中被從新賦值,weak修飾的對象不能夠
static關鍵字的做用
- 函數(方法)體內 static 變量的做用範圍爲該函數體,該變量的內存只被分配一次,所以其值在下次調用時仍維持上次的值;
- 在模塊內的 static 全局變量能夠被模塊內所用函數訪問,但不能被模塊外其它函數訪問;
- 在模塊內的 static 函數只可被這一模塊內的其它函數調用,這個函數的使用範圍被限制在聲明 它的模塊內;
- 在類中的 static 成員變量屬於整個類所擁有,對類的全部對象只有一份拷貝;
- 在類中的 static 成員函數屬於整個類所擁有,這個函數不接收 this 指針,於是只能訪問類的static 成員變量
堆和棧的區別
- 從管理方式來說
- 對於棧來說,是由編譯器自動管理,無需咱們手工控制;
- 對於堆來講,釋放工做由程序員控制,容易產生內存泄露(memory leak)
- 從申請大小大小方面講
- 從數據存儲方面來說
- 棧空間中通常存儲基本類型,對象的地址
- 堆空間通常存放對象自己,block的copy等
風格糾錯題
typedef NS_ENUM(NSInteger, CYLSex)
{
CYLSexMan,
CYLSexWoman
};
@interface CYLUser : NSObject
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, assign, readonly) NSUInteger age;
@property (nonatomic, assign, readwrite) CYLSex sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
- (instancetype)initWithName:(NSString *)name age:(NSUInteger)age;
+ (instancetype)userWithName:(NSString *)name age:(NSUInteger)age sex:(CYLSex)sex;
@end
Objective-C使用什麼機制管理對象內存?
- MRC 手動引用計數
- ARC 自動引用計數,如今一般ARC
- 經過 retainCount 的機制來決定對象是否須要釋放。 每次 runloop 的時候,都會檢查對象的 retainCount,若是retainCount 爲 0,說明該對象沒有地方須要繼續使用了,能夠釋放掉了
ARC經過什麼方式幫助開發者管理內存?
ARC是爲了解決什麼問題誕生的?
- 首先解釋ARC: automatic reference counting自動引用計數
- 瞭解MRC的缺點
- 在MRC時代當咱們要釋放一個堆內存時,首先要肯定指向這個堆空間的指針都被release了
- 釋放指針指向的堆空間,首先要肯定哪些指針指向同一個堆,這些指針只能釋放一次(MRC下即誰建立,誰釋放,避免重複釋放)
- 模塊化操做時,對象可能被多個模塊建立和使用,不能肯定最後由誰去釋放
- 多線程操做時,不肯定哪一個線程最後使用完畢
- 綜上所述,MRC有諸多缺點,很容易形成內存泄露和壞內存的問題,這時蘋果爲儘可能解決這個問題,從而誕生了ARC
ARC下還會存在內存泄露嗎?
- 循環引用會致使內存泄露
- Objective-C對象與CoreFoundation對象進行橋接的時候若是管理不當也會形成內存泄露
- CoreFoundation中的對象不受ARC管理,須要開發者手動釋放
什麼狀況使用weak關鍵字,相比assign有什麼不一樣?
- 首先明白什麼狀況使用weak關鍵字?
- 在ARC中,在有可能出現循環引用的時候,每每要經過讓其中一端使用weak來解決,好比:delegate代理屬性,代理屬性也可以使用assign
- 自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用weak,自定義IBOutlet控件屬性通常也使用weak;固然,也可使用strong,可是建議使用weak
- weak 和 assign的不一樣點
- weak策略在屬性所指的對象遭到摧毀時,系統會將weak修飾的屬性對象的指針指向nil,在OC給nil發消息是不會有什麼問題的;若是使用assign策略在屬性所指的對象遭到摧毀時,屬性對象指針還指向原來的對象,因爲對象已經被銷燬,這時候就產生了野指針,若是這時候在給此對象發送消息,很容形成程序奔潰
- assigin 能夠用於修飾非OC對象,而weak必須用於OC對象
@property 的本質是什麼?
- @property其實就是在編譯階段由編譯器自動幫咱們生成ivar成員變量,getter方法,setter方法
ivar、getter、setter是如何生成並添加到這個類中的?
-
使用「自動合成」( autosynthesis)程序員
-
這個過程由編譯器在編譯階段執行自動合成,因此編輯器裏看不到這些「合成方法」(synthesized method)的源代碼面試
-
除了生成getter、setter方法以外,編譯器還要自動向類中添加成員變量(在屬性名前面加下劃線,以此做爲實例變量的名字)安全
-
爲了搞清屬性是怎麼實現的,反編譯相關的代碼,他大體生成了五個東西多線程
// 該屬性的「偏移量」 (offset),這個偏移量是「硬編碼」 (hardcode),表示該變量距離存放對象的內存區域的起始地址有多遠
OBJC_IVAR_$類名$屬性名稱
// 方法對應的實現函數
setter與getter
// 成員變量列表
ivar_list
// 方法列表
method_list
// 屬性列表
prop_list
- 每次增長一個屬性,系統都會在ivar_list中添加一個成員變量的描述
- 在method_list中增長setter與getter方法的描述
- 在prop_list中增長一個屬性的描述
- 計算該屬性在對象中的偏移量
- 而後給出setter與getter方法對應的實現,在setter方法中從偏移量的位置開始賦值,在getter方法中從偏移量開始取值,爲了可以讀取正確字節數,系統對象偏移量的指針類型進行了類型強轉
@protocol 和 category 中如何使用 @property
- 在protocol中使用property只會生成setter和getter方法聲明,咱們使用屬性的目的,是但願遵照我協議的對象能實現該屬性
- category 使用 @property也是隻會生成setter和getter方法聲明,若是咱們真的須要給category增長屬性的實現,須要藉助於運行時的兩個函數
objc_setAssociatedObject
objc_getAssociatedObject
@property後面能夠有哪些修飾符?
- 原子性—nonatomic特質
- 若是不寫默認狀況爲atomic(系統會自動加上同步鎖,影響性能)
- 在iOS開發中儘可能指定爲nonatomic,這樣有助於提升程序的性能
- 讀/寫權限—readwrite(讀寫)、readooly (只讀)
- 內存管理語義—assign、strong、 weak、unsafe_unretained、copy
- 方法名—getter=、setter=
@property (nonatomic, getter=isOn) BOOL on;
// setter=這種不經常使用,也**不推薦**使用。故不在這裏給出寫法
- 不經常使用的:nonnull,null_resettable,nullable
使用atomic必定是線程安全的嗎?
- 不是,atomic的本意是指屬性的存取方法是線程安全的,並不保證整個對象是線程安全的。
- 舉例:聲明一個NSMutableArray的原子屬性stuff,此時self.stuff 和self.stuff = othersulf都是線程安全的。可是,使用[self.stuff objectAtIndex:index]就不是線程安全的,須要用互斥鎖來保證線程安全性
@synthesize 和 @dynamic分別有什麼做用
- @property有兩個對應的詞,一個是@synthesize,一個是@dynamic。若是@synthesize和@dynamic都沒寫,那麼默認的就是@syntheszie var = _var;
- @synthesize的語義是若是你沒有手動實現setter方法和getter方法,那麼編譯器會自動爲你加上這兩個方法
- @dynamic告訴編譯器:屬性的setter與getter方法由用戶本身實現,不自動生成(固然對於readonly的屬性只需提供getter便可)
- 假如一個屬性被聲明爲@dynamic var,而後你沒有提供@setter方法和@getter方法,編譯的時候沒問題,可是當程序運行到instance.var = someVar,因爲缺setter方法會致使程序崩潰;或者當運行到 someVar = instance.var時,因爲缺getter方法一樣會致使崩潰。編譯時沒問題,運行時才執行相應的方法,這就是所謂的動態綁定
ARC下,不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
- 基本數據:atomic,readwrite,assign
- 普通的OC對象:atomic,readwrite,strong
@synthesize合成實例變量的規則是什麼?假如property名爲foo,存在一個名爲_foo的實例變量,那麼還會自動合成新變量麼?
在有了自動合成屬性實例變量以後,@synthesize還有哪些使用場景?
- 首先的搞清楚什麼狀況下不會autosynthesis(自動合成)
- 同時重寫了setter和getter時
- 重寫了只讀屬性的getter時
- 使用了@dynamic時
- 在 @protocol 中定義的全部屬性
- 在 category 中定義的全部屬性
- 重載的屬性,當你在子類中重載了父類中的屬性,必須 使用@synthesize來手動合成ivar
- 應用場景
- 當你同時重寫了setter和getter時,系統就不會生成ivar)。這時候有兩種選擇
- 手動建立ivar
- 使用@synthesize foo = _foo;,關聯@property與ivar
- 能夠用來修改爲員變量名,通常不建議這麼作,建議使用系統自動生成的成員變量
怎麼用 copy 關鍵字?
- NSString、NSArray、NSDictionary等等常用copy關鍵字,是由於他們有對應的可變類型:NSMutableString、NSMutableArray、NSMutableDictionary,爲確保對象中的屬性值不會無心間變更,應該在設置新屬性值時拷貝一份,保護其封裝性
- block也常用copy關鍵字
- block 使用 copy 是從 MRC 遺留下來的「傳統」,在 MRC 中,方法內部的 block 是在棧區的,使用 copy 能夠把它放到堆區.
- 在ARC中寫不寫都行:對於 block 使用 copy 仍是 strong 效果是同樣的,可是建議寫上copy,由於這樣顯示告知調用者「編譯器會自動對 block 進行了 copy 操做」
用@property聲明的NSString(或NSArray,NSDictionary)常用copy關鍵字,爲何?若是改用strong關鍵字,可能形成什麼問題?
- 由於父類指針能夠指向子類對象,使用copy的目的是爲了讓本對象的屬性不受外界影響,使用copy不管給我傳入是一個可變對象仍是不可對象,我自己持有的就是一個不可變的副本.
- 若是咱們使用是strong,那麼這個屬性就有可能指向一個可變對象,若是這個可變對象在外部被修改了,那麼會影響該屬性.
複製詳解
-
淺複製(shallow copy):在淺複製操做時,對於被複制對象的每一層都是指針複製。
-
深複製(one-level-deep copy):在深複製操做時,對於被複制對象,至少有一層是深複製。
-
徹底複製(real-deep copy):在徹底複製操做時,對於被複制對象的每一層都是對象複製。
-
非集合類對象的copy與mutableCopy
[不可變對象 copy] // 淺複製
[不可變對象 mutableCopy] //深複製
[可變對象 copy] //深複製
[可變對象 mutableCopy] //深複製
-
集合類對象的copy與mutableCopy
[不可變對象 copy] // 淺複製
[不可變對象 mutableCopy] //單層深複製
[可變對象 copy] //單層深複製
[可變對象 mutableCopy] //單層深複製
-
這裏須要注意的是集合對象的內容複製僅限於對象自己,對象元素仍然是指針複製
這個寫法會出什麼問題: @property (copy) NSMutableArray *array;
- 由於copy策略拷貝出來的是一個不可變對象,然而卻把它當成可變對象使用,很容易形成程序奔潰
- 這裏還有一個問題,該屬性使用了同步鎖,會在建立時生成一些額外的代碼用於幫助編寫多線程程序,這會帶來性能問題,經過聲明nonatomic能夠節省這些雖然很小可是沒必要要額外開銷,在iOS開發中應該使用nonatomic替代atomic
如何讓自定義類能夠用 copy 修飾符?如何重寫帶 copy 關鍵字的 setter?
- 若想令本身所寫的對象具備拷貝功能,則需實現NSCopying協議。若是自定義的對象分爲可變版本與不可變版本,那麼就要同時實現NSCopyiog與NSMutableCopying協議,不過通常沒什麼必要,實現NSCopying協議就夠了
// 實現不可變版本拷貝
- (id)copyWithZone:(NSZone *)zone;
// 實現可變版本拷貝
- (id)mutableCopyWithZone:(NSZone *)zone;
// 重寫帶 copy 關鍵字的 setter
- (void)setName:(NSString *)name
{
_name = [name copy];
}
+(void)load; +(void)initialize;有什麼用處?
- +(void)load;
- 當類對象被引入項目時, runtime 會向每個類對象發送 load 消息
- load 方法會在每個類甚至分類被引入時僅調用一次,調用的順序:父類優先於子類, 子類優先於分類
- 因爲 load 方法會在類被import 時調用一次,而這時每每是改變類的行爲的最佳時機,在這裏可使用例如method swizlling 來修改原有的方法
- load 方法不會被類自動繼承
- +(void)initialize;
- 也是在第一次使用這個類的時候會調用這個方法,也就是說 initialize也是懶加載
- 總結:
- 在Objective-C中,runtime會自動調用每一個類的這兩個方法
- +load會在類初始加載時調用
- +initialize會在第一次調用類的類方法或實例方法以前被調用
- 這兩個方法是可選的,且只有在實現了它們時纔會被調用
- 二者的共同點:兩個方法都只會被調用一次
Foundation對象與Core Foundation對象有什麼區別
addObserver:forKeyPath:options:context:各個參數的做用分別是什麼,observer中須要實現哪一個方法才能得到KVO回調?
/**
1\. self.person:要監聽的對象
2\. 參數說明
1> 觀察者,負責處理監聽事件的對象
2> 要監聽的屬性
3> 觀察的選項(觀察新、舊值,也能夠都觀察)
4> 上下文,用於傳遞數據,能夠利用上下文區分不一樣的監聽
*/
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:@"Person Name"];
/**
* 當監控的某個屬性的值改變了就會調用
*
* @param keyPath 監聽的屬性名
* @param object 屬性所屬的對象
* @param change 屬性的修改狀況(屬性原來的值、屬性最新的值)
* @param context 傳遞的上下文數據,與監聽的時候傳遞的一致,能夠利用上下文區分不一樣的監聽
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@"%@對象的%@屬性改變了:%@", object, keyPath, change);
}
KVO內部實現原理
- KVO是基於runtime機制實現的
- 當某個類的屬性對象第一次被觀察時,系統就會在運行期動態地建立該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的setter 方法。派生類在被重寫的setter方法內實現真正的通知機制
- 若是原類爲Person,那麼生成的派生類名爲NSKVONotifying_Person
- 每一個類對象中都有一個isa指針指向當前類,當一個類對象的第一次被觀察,那麼系統會偷偷將isa指針指向動態生成的派生類,從而在給被監控屬性賦值時執行的是派生類的setter方法
- 鍵值觀察通知依賴於NSObject 的兩個方法: willChangeValueForKey: 和 didChangevlueForKey:;在一個被觀察屬性發生改變以前, willChangeValueForKey: 必定會被調用,這就 會記錄舊的值。而當改變發生後,didChangeValueForKey: 會被調用,繼而 observeValueForKey:ofObject:change:context: 也會被調用。
- 補充:KVO的這套實現機制中蘋果還偷偷重寫了class方法,讓咱們誤認爲仍是使用的當前類,從而達到隱藏生成的派生類
如何手動觸發一個value的KVO
- 自動觸發的場景:在註冊KVO以前設置一個初始值,註冊以後,設置一個不同的值,就能夠觸發了
- 想知道如何手動觸發,必須知道自動觸發 KVO 的原理,見上面的描述
- 手動觸發演示
@property (nonatomic, strong) NSDate *now;
- (void)viewDidLoad
{
[super viewDidLoad];
// 「手動觸發self.now的KVO」,必寫。
[self willChangeValueForKey:@"now"];
// 「手動觸發self.now的KVO」,必寫。
[self didChangeValueForKey:@"now"];
}
若一個類有實例變量NSString *_foo,調用setValue:forKey:時,是以foo仍是_foo做爲key?
KVC的keyPath中的集合運算符如何使用?
- 必須用在集合對象上或普通對象的集合屬性上
- 簡單集合運算符有@avg, @count , @max , @min ,@sum
KVC和KVO的keyPath必定是屬性麼?
文末推薦:iOS熱門文集&視頻解析
-
① iOS底層技術
-
② iOS逆向防禦
-
③ 大廠面試題+底層技術+逆向安防+Swift