iOS知識集錦(持續(xù)更新)

ruby.taobao.org 停止更新了,使用 cocoapods 的童鞋,更新到 ruby china 的源

$ gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/
$ gem sources -lhttps://gems.ruby-china.org # 確保只有 gems.ruby-china.org
$ gem sources -l 
*** CURRENT SOURCES ***
https://gems.ruby-china.org
# 請確保只有 gems.ruby-china.org
$ gem install rails

$ gem install rails時出現(xiàn)這樣的錯誤:

yitudevdeiMac:~ yitudev$ gem install rails
Building native extensions.  This could take a while...
ERROR:  While executing gem ... (Errno::EACCES)
    Permission denied @ dir_s_mkdir - /usr/local/lib/ruby/gems/2.3.0/extensions/x86_64-darwin-15/2.3.0/nokogiri-1.6.8.1
yitudevdeiMac:~ yitudev$ gem install rails
Building native extensions.  This could take a while...

進行操作:

yitudevdeiMac:~ yitudev$ sudo gem install cocoapods-core 

再$ gem install rails。當pod setup 進入Setting up CocoaPods master repo 等待的時候表示正在下載了,此時你可通過新開一個終端窗口,輸入"cd ~/.cocoapods/"命令行跳到cocoapods文件夾內(nèi),執(zhí)行"du -sh *"查看正在下載的文件夾的大小。
CocoaPods 的簡單快速安裝方法(Setting up CocoaPods master repo 卡著不動,是因為淘寶鏡像已經(jīng)不能用了)

  • 使用CocoaPods來添加第三方類庫,無論是執(zhí)行pod install還是pod update都卡在了Analyzing dependencies不動原因在于當執(zhí)行以上兩個命令的時候會升級CocoaPods的spec倉庫,加一個參數(shù)可以省略這一步,然后速度就會提升不少。(最近進行install 和 update,有時即使加了--no-repo-update還是死慢)
pod install --verbose --no-repo-update
pod update --verbose --no-repo-update

NULL、nil、Nil、NSNull區(qū)別

標志 含義
NULL (void *)0 C 指針的字面空值
nil (id)0 Objective-C 對象的字面空值
Nil (Class)0 Objective-C 類的字面空值
NSNull [NSNull null] 用來表示空值的 Objective-C 對象
  • NULL一般用于表示 C 指針空值,如:
int *pointerToInt = NULL;
char *pointerToChar = NULL;
struct TreeNode *rootNode = NULL;
  • nil:
NSString *someString = nil;
NSURL *someURL = nil;
id someObject = nil;
if (anotherObject == nil) 
  • Nil:
Class someClass = Nil;
Class anotherClass = [NSString class];
  • NSNull
    是一個 Objective-C 對象,是一個用于表示空值的類,而且它只有一個單例方法:+[NSNull null],一般用于在集合對象中保存一個空的占位對象。
// 當 NSArray 里遇到 nil 時,就說明這個數(shù)組對象的元素截止了,即 NSArray 只關(guān)注 nil 之前的對象,nil 之后的對象會被拋棄。
NSArray *array = [NSArray arrayWithObjects:@"one", @"two", nil];

// 錯誤的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:nil forKey:@"someKey"];

// 正確的使用
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSNull null] forKey:@"someKey"];

取消延遲執(zhí)行函數(shù)

  • 法一:performSelector
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;  
- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay;  
// 取消某一個延時調(diào)用請求
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument; 
// 取消performSelector的所有被延遲執(zhí)行的方法 
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget; 
  • 法二:創(chuàng)建定時器
// 創(chuàng)建定時器的time-類方法,需要手動fire開啟定時器,將執(zhí)行方法封裝到NSInvocation中
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
// 創(chuàng)建定時器的time-類方法,需要手動fire開啟定時器
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
// 創(chuàng)建定時器的scheduled-類方法,不需要手動fire開啟定時器,將執(zhí)行方法封裝到NSInvocation中
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
// 創(chuàng)建定時器的scheduled-類方法,不需要手動fire開啟定時器
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
// 創(chuàng)建定時器的實例方法,會在指定時間開啟定時器
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(id)ui repeats:(BOOL)rep;
// 開啟定時器
- (void)fire;
// 使定時器失效,將定時器從循環(huán)池移除掉
- (void)invalidate;

self.timer = [NSTimer scheduledTimerWithTimeInterval:3.0f target:self selector:@selector(prtintMyWords:) userInfo:@{@"words":@"hello world"} repeats:NO];
[self.timer invalidate];
  • 法三:使用GCD
    取消GCD的延遲執(zhí)行還不知道如何操作,可參考Dispatch-Cancel
// 創(chuàng)建延遲時間,從當前時間開始,3秒后執(zhí)行。 3秒需要轉(zhuǎn)化為納秒,因為該函數(shù)是以納秒為基礎(chǔ)進行的
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, 3.0*NSEC_PER_SEC);
// 執(zhí)行延遲函數(shù)
dispatch_after(delay, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   [self printString:@"hello world"];
});
  • 注意:
    1、performSelector和scheduledTimerWithTimeInterval方法都是基于runloop的。我們知道,當一個應(yīng)用啟動時,系統(tǒng)會開啟一個主線程,并且把主線程的runloop激活,也就是run起來,并且主線程的runloop是不會停止的。所以,當這兩個方法在主線程可以被正常調(diào)用。但情況往往不是這樣的。實際編碼中,我們更多的邏輯是放在子線程中執(zhí)行的。而子線程的runloop是默認關(guān)閉的。這時如果不手動激活runloop,performSelector和scheduledTimerWithTimeInterval的調(diào)用將是無效的。
    2、NSTimer、performSelector的創(chuàng)建與撤銷必須在同一個線程操作。
    3、當一個timer被schedule的時候,timer會持有target對象,NSRunLoop對象會持有timer。當invalidate被調(diào)用時,NSRunLoop對象會釋放對timer的持有,timer會釋放對target的持有。除此之外,我們沒有途徑可以釋放timer對target的持有。所以解決內(nèi)存泄露就必須撤銷timer,若不撤銷,target對象將永遠無法釋放。
    4、若使用dispatch_after,系統(tǒng)會幫我們處理線程級的邏輯,這樣也我們更易于享受系統(tǒng)對線程所做的優(yōu)化。除此之外,我們不用關(guān)心runloop的問題。并且調(diào)用的對象也不會被強行持有,這樣上述的內(nèi)存問題也不復存在。當然,需要注意block會持有其傳入的對象,但這可以通過weakself解決。所以在這種延遲操作方案中,使用dispatch_after更佳。

版本號如何控制

GNU 風格的版本號管理策略:一般的版本號劃分為如下3個部分——“主版本號 . 子版本號 . 修正版本號”,簡單直觀清晰。
1、項目初版本時,版本號可以為 0.1 或 0.1.0, 也可以為 1.0 或 1.0.0 (例如: 1.0.0 )
2、當項目在進行了局部修改或 bug 修正時,主版本號和子版本號都不變,修正版本號加 1 (例如: 1.0.1 )
3、當項目在原有的基礎(chǔ)上增加了部分功能時,主版本號不變,子版本號加 1,修正版本號復位為 0 (例如: 1.1.0 )
4、當項目在進行了重大修改或局部修正累積較多,而導致項目整體發(fā)生全局變化時,主版本號加 1 (例如: 2.0.0 )
5、另外,編譯版本號一般是編譯器在編譯過程中自動生成的,我們只定義其格式,并不進行人為控制。

直接調(diào)用方法和使用performSelector調(diào)用的區(qū)別

1、兩種方式執(zhí)行的效果其實和發(fā)送消息是等價的。
2、performSelector允許發(fā)送未在運行時確定的消息;也就是說,只要這個消息能夠被轉(zhuǎn)發(fā)到正確的接收者,能夠被最后的接收者識別,都是可以正確運行的。否則,就會在運行時報錯“unrecognized selector sent to instance”。如果沒有實現(xiàn)該方法,只能在運行時才會報錯,編譯階段發(fā)現(xiàn)不了。
3、Obejct-C的動態(tài)特性是允許在運行時向某個類添加方法的,這個時候就一定需要使用performSelector。那么為了避免運行時出現(xiàn)錯誤,在使用performSelector之前一定要使用如下的檢查方法來進行判斷。我們一般都忽略的用該方法進行判斷。

- (BOOL)respondsToSelector:(SEL)aSelector;

#pragma 處理防止編譯器警告

  • 首先#pragma在本質(zhì)上是聲明,常用的功能就是注釋,尤其是給Code分段注釋;而且它還有另一個強大的功能是處理編譯器警告,但卻沒有上一個功能用的那么多。
    clang diagnostic 是#pragma 第一個常用命令:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-相關(guān)命令" 
// 你自己的代碼
#pragma clang diagnostic pop
#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wdeprecated-declarations" 
[TestFlight setDeviceIdentifier:[[UIDevice currentDevice] uniqueIdentifier]]; 
#pragma clang diagnostic pop

2、不兼容指針類型

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wincompatible-pointer-types"  
// 
#pragma clang diagnostic pop

3、循環(huán)引用

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Warc-retain-cycles"  
  self.completionBlock = ^ {
     ... 
  }; 
#pragma clang diagnostic pop

4、未使用變量

#pragma clang diagnostic push 
#pragma clang diagnostic ignored "-Wunused-variable"  
int a; 
#pragma clang diagnostic pop

5、參數(shù)不固定的情況:以DKNightVersion為例

DKImagePicker DKImagePickerWithNames(NSString *normalName, ...) {
    NSArray<DKThemeVersion *> *themes = [DKColorTable sharedColorTable].themes;
    NSMutableArray<NSString *> *names = [[NSMutableArray alloc] initWithCapacity:themes.count];
    [names addObject:normalName];
    NSUInteger num_args = themes.count - 1;
    va_list names_list;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wvarargs"
    va_start(names_list, num_args);
#pragma clang diagnostic pop
    for (NSUInteger i = 0; i < num_args; i++) {
        NSString *name = va_arg(names_list, NSString *);
        [names addObject:name];
    }
    va_end(names_list);

    return [DKImage pickerWithNames:names];
}

va_arg(va_list ap, type),參數(shù)ap應(yīng)該首先被宏va_start 或 va_copy初始化,但又必須在被宏va_end調(diào)用之前使用。每次調(diào)用va_arg都會改變ap值使得后續(xù)的參數(shù)值能被依次添加。參數(shù)type應(yīng)該是一個類型名,并且用type*能夠得到該類型的指針類型。如果type為空,或者type和實際參數(shù)不匹配, 那么除了以下兩種情況,這個宏的行為是未定義的。
用法:
(1)首先在函數(shù)里定義一具VA_LIST型的變量,這個變量是指向參數(shù)的指針;
(2)然后用VA_START宏初始化剛定義的VA_LIST變量;
(3)然后用VA_ARG返回可變的參數(shù),VA_ARG的第二個參數(shù)是你要返回的參數(shù)的類型(如果函數(shù)有多個可變參數(shù)的,依次調(diào)用VA_ARG獲取各個參數(shù));
(4)最后用VA_END宏結(jié)束可變參數(shù)的獲取。

在對象內(nèi)部盡量直接訪問實例變量

  • 在對象內(nèi)部讀取數(shù)據(jù)時,應(yīng)該直接通過實例變量來讀,而寫入數(shù)據(jù)時,應(yīng)該通過屬性來寫。
  • 在初始化及dealloc方法中,總是應(yīng)該直接通過實例變量來讀寫數(shù)據(jù)。

消息傳遞、轉(zhuǎn)發(fā)機制

  • objc_msgSend依據(jù)接收者和選擇子的類型來調(diào)用適當?shù)姆椒āT摲椒ㄐ枰诮邮照咚鶎俚念愔兴褜て洹胺椒斜怼?,如能找到與選擇子名稱相符的方法,就跳轉(zhuǎn)至其實現(xiàn)代碼。若找不到,沿著繼承體系繼續(xù)向上查找,等找到合適的方法后再跳轉(zhuǎn)。如最終還是找不到相符的方法,就執(zhí)行“消息轉(zhuǎn)發(fā)”操作。
  • 消息轉(zhuǎn)發(fā)分兩步:
    1、先征詢接收者所屬的類,看其是否能動態(tài)添加方法,以處理當前這個“未知的選擇子”,這叫做“動態(tài)方法解析”。
    2、涉及“完整的消息轉(zhuǎn)發(fā)機制”,分兩步:接收者看有沒有其他對象能處理這條消息,若有,則運行期系統(tǒng)會把消息轉(zhuǎn)給那個對象,消息轉(zhuǎn)發(fā)結(jié)束。若沒有“備援的接收者”,則啟動完整的消息轉(zhuǎn)發(fā)機制,運行期系統(tǒng)會把消息有關(guān)的全部細節(jié)封裝到NSInvocation中,再給接收者最后一次機會,令其設(shè)法解決當前還未處理的這條消息。

isMemberOfClass:與isKindOfClass:區(qū)別

  • “isMemberOfClass:”能夠判斷出對象是否為某個特定類的實例;
  • “isKindOfClass:”能夠判斷對象是否為某類或其派生類的實例。
  • 盡量使用類型信息查詢方法來確定對象類型,而不要直接比較類對象,因為某些對象可能實現(xiàn)了消息轉(zhuǎn)發(fā)功能。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容