趁着開年的空閒時間,找了一些面試題寫寫, 水平渣,僅做參考!,歡迎指導討論,寫的不對的地方望及時提出! 原題在這裏html
中級篇請看這裏: iOS常見面試題(block,runtime,runloop,類結構)附參考答案ios
runtime
運行時機制能夠作到純靜態語言作不到的事情:例如動態地增長、刪除、替換ivar
或者方法等weak
表示該對象並不持有該delegate
對象,delegate
對象的銷燬由外部控制;若是用strong
則該對象強引用delegate
,外界不能銷燬delegate
對象,會致使循環引用(Retain Cycles)
delegate
是委託的意思,在OC
中表示一個類委託另外一個類實現某個方法。當一個對象接受到某個事件或者通知的時候,會向它的delegate
對象查詢它是否可以響應這個事件或者通知,若是能夠這個對象就會給它的delegate
對象發送一個消息(執行一個方法調用)。 datasource
字面是數據源,通常和delegate
伴生,這時數據源處理的數據就是delegate
中發送委託的類中的數據,並經過datasource
發送給接受委託的類。 Instead of being delegated control of the user interface, a data source is delegated control of data.
官網上這句話解釋的比較好,咱們能夠發現,delegate
控制的是UI,是上層的東西;而datasource
控制的是數據。他們本質都是回調,只是回調的對象不一樣。官網原文delegate
和block
均可以實現回調傳值。block
寫法簡練,能夠直接訪問上下文,代碼閱讀性好,適合與狀態無關的操做,更加面向結果,使用過程當中須要注意避免形成循環引用。delegate
更像一個生產流水線,每一個回調方法是生產線上的一個處理步驟,一個回調的變更可能會引發另外一個回調的變更,其更加面向過程,當有多個相關方法時建議使用delegate
。@"property" = ivar + getter + setter
nonatomic
和atomic
readwrite
和readonly
assign
:修飾簡單數據類型。 weak
:弱引用,多數用來修飾delegate
和outlet
。 copy
:拷貝,多用於修飾NSString
、block
等,其做爲屬性修飾符時是將_property
先release (_property release)
,而後拷貝參數內容(_property copy)
,建立一塊新的內存地址,最後_property = property
。strong:強引用,其做爲屬性修飾符時是將_property
先release (_property release)
,而後將參數retain(_property retain)
,最後_property = property
。retain
:MRC下特有,等同於strong
。unsafe_unretained
:和weak
同樣,惟一的區別即是,對象即便被銷燬,指針也不會自動置空, 此時指針指向的是一個無用的野地址。若是使用此指針,程序會拋出 BAD_ACCESS
的異常。@synthesize
讓編譯器自動生成getter/setter
方法。當有自定義的存取方法時,會覆蓋該方法@dynamic
告訴編譯器,不自動生成getter/setter
方法。由本身實現存取方法或存取方法在運行時動態建立綁定ARC下:面試
atomic,readwrite,assign
atomic,readwrite,strong
MRC下:設計模式
atomic,readwrite,assign
atomic,readwrite,retain
@interface Person : NSObject
@property(nonatomic ,copy)NSString *cpName;
@property(nonatomic ,strong)NSString *stName;
@end
@implementation Person
@end
main(){
NSMutableString *string = [NSMutableString stringWithFormat:@"name"];
Person *person = [[Person alloc]init];
person.cpName = string;
person.stName = string;
[string appendString:@"name"];
NSLog(@"cpName:%@ stName:%@",person.cpName,person.stName);
}
複製代碼
打印結果以下:
cpName:name stName:namename複製代碼
至於爲何要用copy, 大概是爲了防止mutableString被無心中修改。緩存
NSCopying
協議。若是自定義的對象分爲可變版本與不可變版本,那麼就要同時實現 NSCopying
與 NSMutableCopying
協議。//不可變字符串
NSString *string = @"string";
NSString *cpString1 = [string copy]; //指針拷貝
NSString *mcpString1 = [string mutableCopy]; //內容拷貝,生成可變對象
NSMutableString *mstr = [string mutableCopy]; //同上
NSLog(@"%p %p %p %p",string,cpString1,mcpString1,mstr);
打印結果以下:
0x100001070 0x100001070 0x10051bbc0 0x100600e10
//可變字符串
NSMutableString *mstr1 = [[NSMutableString alloc]initWithString:@"abcde"];
NSMutableString *mstr2 = [mstr1 copy]; //內容拷貝,生成不可變字符串
NSString *cpstring2 = [mstr1 copy]; //同上
NSMutableString *mstr3 = [mstr1 mutableCopy]; //內容拷貝
NSLog(@"%p %p %p %p",mstr1,mstr2, cpstring2,mstr3);
打印結果以下:
0x102063110 0x656463626155 0x656463626155 0x102063160複製代碼
NS* NSMutable*
等集合類的copy、mutableCopy
同上述同樣,須要注意的是,集合裏面的元素並無內容拷貝!若集合層級不少,且須要徹底內容拷貝,能夠利用NSKeyedArchiver
實現。安全
防止 更新:感謝各位指出錯誤!網絡(Retain Cycles)
在storyboard或者xib中建立的UIView,自己會被它的superView
強引用,以UILable
爲例子:多線程
UIViewController -> UIView -> subView -> UILable
架構
此時控件拖線會默認爲weak
屬性,由於UIlable
已經被UIView
擁有,當UIViewController釋放的時候,UIView釋放,UILable才能夠釋放,因此正常狀況下UILable和UIView的生命週期是同樣的。設置成strong
也沒什麼大問題, 可是當UILable從其父視圖UIView上remove
掉,UIViewController對其還有一個strong
強引用,UILable沒法釋放,這時就比較尷尬了...併發
nonatomic
:表示非原子性,不安全,可是效率高。atomic
:表示原子性,安全,可是效率較低。atomic
:經過鎖定機制來確保其原子性,但只是讀/寫安全,不能絕對保證線程的安全,當多線程同時訪問的時候,會形成線程不安全。可使用線程鎖來保證線程的安全。UICollectionViewLayout
是爲向UICollectionView
提供佈局信息的類,包括cell
的佈局信息等。UICollectionView
的自定義佈局能夠分爲三種方式:thread
performSelector
函數- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;複製代碼
dispatch_async(otherQueue, ^{
// dosth
});
複製代碼
[otherQueue addOperationWithBlock:^{
// dosth
}];複製代碼
group
: 當全部任務都執行完成以後,才執行dispatch_group_notify
中的任務 dosthNotify。- (void)gcdGroup{
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// dosth1;
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// dosth2;
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// dosthNotify;
});
}複製代碼
barrier
: 柵欄方法,當柵欄前一組操做執行完以後,才能開始執行後一組方法- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// dosth1;
});
dispatch_async(queue, ^{
// dosth2;
});
dispatch_barrier_async(queue, ^{
// doBarrier;
});
dispatch_async(queue, ^{
// dosth4;
});
dispatch_async(queue, ^{
// dosth5;
});
}複製代碼
dispatch_semaphore
。dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(queue, ^{
// dosth1
// 使信號量+1並返回
dispatch_semaphore_signal(semaphore);
});
// 若信號的信號量爲0,則會阻塞當前線程,直到信號量大於0或者通過輸入的時間值;若信號量大於0,則會使信號量減1並返回,程序繼續住下執行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// dosth2 ,只有當dosth1執行完,信號量+1以後,纔會執行這裏
複製代碼
// 當應用程序啓動時(不包括已在後臺的狀況下轉到前臺),調用此回調。launchOptions是啓動參數,假如用戶經過點擊push通知啓動的應用,這個參數裏會存儲一些push通知的信息
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions NS_AVAILABLE_IOS(3_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
//應用即將從前臺狀態轉入後臺
- (void)applicationWillResignActive:(UIApplication *)application;
– (void)applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
//從後臺到前臺調用了:
– (void)applicationWillEnterForeground:(UIApplication *)application NS_AVAILABLE_IOS(4_0);
– (void)applicationDidBecomeActive:(UIApplication *)application;
複製代碼
App啓動過程以下:
NSObject *object = [[NSObject alloc] init];複製代碼
類方法+ alloc
,其根據要建立的實例對象對應的類來分配足夠的內存空間。除了分配內存空間,其實+ alloc
方法還作了其餘事情,包括將對象的引用計數記爲1,將對象的isa指針指向對應的運行時類對象,以及將對象的成員變量置爲對應的0值(0、nil、NULL)。可是+ alloc
方法返回的對象仍是不可用的,在以後完成初始化方法的調用後,對象的建立工做纔算完成。初始化方法會設置對象的成員變量爲一個正確的合理的值,以及獲取一些其餘額外的資源。
全部對象都是要初始化的,並且不少狀況下,對象在初始化時是須要接收額外的參數,這就可能會提供多個初始化方法。
- (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
- (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;複製代碼
根據規範,一般選擇一個接收參數最多的初始化方法做爲指定初始化方法,真正的數據分配和其餘相關初始化操做在這個方法中完成。而其餘的初始化方法則做爲便捷初始化方法去調用這個指定初始化方法。這樣當實現改變時,只要修改指定初始化方法就能夠了。便捷初始化方法接收的參數更少,它會在內部調用指定初始化方法時,直接設置未接收參數的默認值。便捷初始化方法也能夠不直接調用指定初始化方法,它能夠調用其餘便捷初始化方法,但無論調用幾層,最終是要調用到指定初始化方法的,由於真正的實現操做是在指定初始化方法中完成的。全部初始化方法統一以- init開始。如上例代碼所示,- initWithTimeIntervalSinceReferenceDate
方法是一個指定初始化方法,而其餘初始化方法最終是要調用它的。
當子類繼承父類後實現了新的指定初始化方法,此時若是調用父類中的指定初始化方法則沒法調用到子類新實現的初始化邏輯,因此子類同時還要重寫父類的指定初始化方法,將其變爲一個便捷初始化方法,最終去調用子類本身的指定初始化方法。而爲了保證父類初始化邏輯的執行,在子類指定初始化方法中,首先要經過關鍵字super調用父類的指定初始化方法。 詳見正確編寫Designated Initializer的幾個原則
若是父類也實現了協議,首先要調用父類的- initWithCoder:方法,若是父類沒有實現,則調用父類的指定初始化方法。
當在接口中指定初始化方法的後面加上該宏,編譯器就會檢查咱們實現的初始化調用鏈是否符合規則,並提示相應的警告。
- (instancetype)init NS_DESIGNATED_INITIALIZER;複製代碼