iOS小知識積累(長期更新)

以前工作中有很多小的知識點,但是有時候只是用了,沒有真正積累下來,有時候也會忘記。所以寫這篇文章就是慢慢的將以前小的知識點或者之后用到的用文字的形式記錄下來?!敬a字、分享不容易,各位的關(guān)注是小的動力!動動親的小手指關(guān)注一下我哈?!?/em>
1、_Nullable和_Nonnull(Xcode 6.3 )
_Nullable表示可能是NULL或nil;_Nonnull表示不應(yīng)該是NULL或nil。如果不遵循會有警告
如果需要每個屬性或每個方法都去指定nonnull和nullable,是一件非常繁瑣的事。蘋果為了減輕我們的工作量,專門提供了兩個宏:NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END。
2、__kindof(Xcode 6.3 )
表示我們不用強轉(zhuǎn)類型了。比如:

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

我們我可以直接用某個cell接收,不用再寫(SomeCell*)這種形式強轉(zhuǎn)了。

@property (nonatomic, readonly, copy) NSArray<__kindof UIView *> *subviews;

這樣,寫下面的代碼時就沒有任何警告了:

UIButton *button = view.subviews.lastObject;

3、instancetype、id、NSObject
instancetype 與 id 不一樣, instancetype 只能在方法聲明中作為返回類型使用。我們寫方法返回的時候用instancetype而不用id了。instancetype代表當前類的類型。

  • id關(guān)鍵字在編譯時不被檢查,而NSObject在編譯時會被檢查是否被調(diào)用一些錯誤方法。
  • id可以是任何對象,包括非NSObject對象
  • 定義id的時候不使用*,是一個指針,NSObject卻需要。

4、Storyboard References (iOS 9)
可以幫我們簡化Storyboard,平時我們的做法是將一個大的Storyboard人為的去分成幾個小的,然后代碼組裝起來。Storyboard References可以幫我們簡化這一步。如果本身有一個Storyboard很負責(zé),可以選擇其中聯(lián)系很大的一部分,然后點擊 Xcode 的菜單欄,選擇"Editor->Refactor to Storyboard"。這時候就有一個獨立的Storyboard了。
5、打印當前ViewController 的名字
這個對于我們拿到一個陌生的代碼,調(diào)試的時候非常方便。比如我們運行一個APP,看到一個界面,但是想知道對應(yīng)哪個ViewController的時候。

  • 添加Symbolic Exception


    A7AB9D87-CF83-4F63-8D66-9C5A113B76ED.png
  • 添加Symob和Action


    73D79C18-5E28-4A87-B039-4DDA566FD5F7.png

6、Autolayout中Intrinstic Size、Compression Resistance Priority、Compression Resistance Priority

  • Intrinstic Size就是不用設(shè)置frame,視圖通過內(nèi)容、字體大小等屬性就能獲得的大小。我們設(shè)置的Content Hugging Priority和Compression Resistance Priority都是針對于這個的。
  • Content Hugging Priority 內(nèi)容擁抱,就是想變小的優(yōu)先級
  • Compression Resistance Priority抗壓縮,就是想變大的優(yōu)先級
A90EC537-5342-47B0-8207-9F3DBC0AB8A3.png

如上圖視圖內(nèi)容比文字內(nèi)容寬,這個時候我們想要視圖變小,與文字大小一樣(此時intrinsic的大小就是文字區(qū)域部分的大?。?。所以只要改變Content Hugging Priority的Horizontal的優(yōu)先級變大就好了
7、topLayoutGuide&& bottomLayoutGuide
這兩個屬性可以理解為性表示不希望被透明的狀態(tài)欄或?qū)Ш綑冢║ITabBar、UIToolBar、NavigationBar等)遮擋的內(nèi)容范圍的最高或最低位置。這個屬性的值是它的length屬性的值。如下:

[view mas_makeConstraints:^(MASConstraintMaker *make) {
    make.top.mas_equalTo(self.mas_topLayoutGuideBottom);
    make.leading.mas_equalTo(self.view.mas_leading);
    make.trailing.mas_equalTo(self.view.mas_trailing);
    make.bottom.mas_equalTo(self.mas_bottomLayoutGuideTop);
}];

8、Autolayout中約束LayoutConstraint的有效無效(8.0)
有時候我們經(jīng)常遇到一個view在不同的場景下約束不同,通常做法就是在不同場景下先刪除再增加這樣操作,這樣效率反而不高。我們可以通過active這個屬性設(shè)置生效不生效。

self.mLayoutConstraintWidth.active = YES;

9、隱藏導(dǎo)航欄返回按鈕旁邊的文字

[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -60) forBarMetrics:UIBarMetricsDefault];

10、替換返回按鈕圖片

UIImage *bgImage = [[UIImage imageNamed:@"return"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[[UINavigationBar appearance] setBackIndicatorImage:bgImage];
[[UINavigationBar appearance] setBackIndicatorTransitionMaskImage:bgImage];

11、頭文件導(dǎo)入
@class 在.h文件時引入其他.h文件的時候,效率更高

include "" 自己寫的文件

include <> 引入系統(tǒng)的

import <> 用于包含系統(tǒng)文件

import ""自己寫的文件,搜索路徑更廣

import比include更好,能夠避免文件的重復(fù)導(dǎo)入,效率更高
不過還有一個更好的的方式@import,這種方式讓我們不必在設(shè)置里邊添加包了,它會幫我們自動關(guān)聯(lián)。
12、內(nèi)存管理語義

  • assign "設(shè)置方法"只會執(zhí)行針對"純量類型"(例如CGFloat或NSInteger等)的簡單賦值操作
  • strong 此特質(zhì)表明該屬性定義了一種"擁有關(guān)系"。為這種屬性設(shè)置新值時,設(shè)置方法會先保留新值,并釋放舊值,然后再將新值設(shè)置上去。
  • weak (5.0以上,可以說是對unsafe_unretained的一個升級,更安全,但是由于要遍歷是否已經(jīng)銷毀,并且設(shè)置成nil,所以是消耗性能)此特質(zhì)表明該屬性定義了一種"非擁有關(guān)系"。為這種屬性設(shè)置新值時,設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)同assign類似,然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil)。
  • unsafe_unretained (5.0以下必須使用而不能使用weak,優(yōu)點比weak快,缺點容易造成野指針)此特質(zhì)的語義和assign相同,但是它使用于"對象類型",該特質(zhì)表達一種"非擁有關(guān)系",當對象遭到摧毀時,屬性值不會自動清空("不安全",unsafe)這一點與weak有區(qū)別。
  • copy 此特質(zhì)所表達的所屬關(guān)系與strong類似。然而設(shè)置方法并不保留新值,而是將其"拷貝"。警惕NSMutableArray為copy的時候奔潰問題。copy之后是NSArray添加對象會crash。
    13、查找或判斷xib是否存在
NSString *path = [[NSBundle mainBundle] pathForResource:NSStringFromClass(className) ofType:@"nib"];
Boolean exist = [[NSFileManager defaultManager] fileExistsAtPath:path];

備注:其實關(guān)鍵就是Typ是"nib"而不是"xib"。因為我們打出的包,系統(tǒng)將我們的xib文件編譯成了以nib為結(jié)尾的文件。
14、UI_APPEARANCE_SELECTOR
當我們自定義控件的時候可以在屬性后邊增加這個聲明。代表支持appearance方法。如果你自己調(diào)用了setXX方法,則appearance方法失效。

@property (nonatomic, assign) CGFloat selectionIndicatorHeight UI_APPEARANCE_SELECTOR;
[XXXClass appearance].selectionIndicatorHeight = xxxx

15、旋轉(zhuǎn)動畫方向問題

button.transform = CGAffineTransformRotate(button.transform,M_PI);

我們先旋轉(zhuǎn)180順時針

button.transform = CGAffineTransformIdentity;

當我們調(diào)用上句話的時候,是順時針旋轉(zhuǎn)180返回原位置,但是很多時候我們想原路返回,其實就是想逆時針返回。

button.transform = CGAffineTransformRotate(button.transform,M_PI-0.0001);

只要這么設(shè)置就會逆時針返回了。旋轉(zhuǎn)動畫選擇了最短路徑返回

16、純代碼布局,添加約束
如果我們使用純代碼布局,則需要以下代碼約束才能起作用;默認左右是有16像素間距的,設(shè)置左右各-16才能靠邊(這個如果有其他好辦法可以留言給我)

self.view.translatesAutoresizingMaskIntoConstraints = NO;

17、NSDate和NSDateFormatter
NSDate:是獲取的時間沒有時區(qū)概念,所以獲取的是0時區(qū)的時間。

[NSDate date]//獲取的時間與當前時間差8個小時

NSDateFormatter:將日期格式成我們想要的。同時會根據(jù)時區(qū)、地域給我們做相應(yīng)的計算。(看注釋)

NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSTimeZone *timeZone = [NSTimeZone localTimeZone];
//  formatter.locale = [NSLocale currentLocale];
 formatter.timeZone = timeZone;
NSDate *currentDate = [NSDate date];
NSString *currentString = [formatter stringFromDate:currentDate];//會將currentDate增加8個小時,獲取我們當前時區(qū)的時間
NSDate *date = [formatter dateFromString:@"2017-8-31 9:51:00"];//獲取的date是針對0時區(qū)的時間,所以會減去8個小時

以上代碼可以看到我們可以設(shè)置timeZone和locale。如果我們只設(shè)置local照樣能夠輸出我們想要的結(jié)果。但是二者有什么區(qū)別呢?

  • local是設(shè)置區(qū)域的。配合setTimeStyle:、setDateStyle:這些方法可以控制日期輸出不同的格式。因為每個地方的格式不同。 Formatting Data Using the Locale Settings
7FAEF832-CBCD-41A2-A4EE-4056FBF1B931.png
  • timeZone設(shè)置時區(qū)的。主要用來將時間轉(zhuǎn)換成不同時區(qū)。

所以在代碼格式化的時候,建議設(shè)置timezone來格式化我們的時期

18、Xcode關(guān)閉控制臺打印不用信息

Xcode->Edit Scheme -> Run -> Arguments, 在Environment Variables里邊添加 OS_ACTIVITY_MODE = disable

19、NSObject中isKindOfClass、isMemberOfClass、isSubclassOfClass區(qū)別

isSubclassOfClass和isKindOfClass的作用基本上是一致的,只不過一個是類方法,一個是對象方法。
isKindOfClass來確定一個對象是否是一個類的成員,或者是派生自該類的成員,
isMemberOfClass只能確定一個對象是否是當前類的成員.
isMemberOfClass 篩選條件更為苛刻,只有當類型完全匹配的時候才會返回YES。

20、判斷當前queue是否是main queue

  static void *mainQueueKey = &mainQueueKey;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    dispatch_queue_set_specific(dispatch_get_main_queue(),
                                mainQueueKey, mainQueueKey, NULL);
  });
  return dispatch_get_specific(mainQueueKey) == mainQueueKey;

21、為什么有的屬性要用copy,而不用strong

像NSString和NSArray這兩類,在給變量賦值的時候,如果來源是可變字符串或可變數(shù)組的時候,使用copy能夠避免原來變量對現(xiàn)在變量的影響。如果用strong的話,原來變量改變時同時會影響我們現(xiàn)在的變量。我們大多數(shù)期望的結(jié)果是兩個變量相互獨立,互補影響的,所以使用copy能夠避免這類問題
22、SEL、IMP

SEL 函數(shù)的ID,相當于將函數(shù)的名字用一個唯一識別符與之相對應(yīng)。
IMP 本質(zhì)是函數(shù)指針。其實就是函數(shù)的實現(xiàn),我們在調(diào)用方法的時候是一層一層的查找,最終找到了IMP,如果直接調(diào)用IMP將會更有效率

23、消息的轉(zhuǎn)發(fā)

44181-09a882edcb98d535.jpg

runtime在調(diào)用方法的時候如果找不到會直接奔潰。如果在當前類找不到的時候,會開始順序的調(diào)用系統(tǒng)的三個方法,如果再找不到會直接crash。

1.+(Bool) resolveInstanceMethod:(SEL)sel // 對應(yīng)實例方法
  + (Bool)resolveClassMethod:(SEL)sel // 對應(yīng)類方法
我們可以在這個方法中調(diào)用class_addMethod將方法加入就不會crash
2.- (id)forwardingTargetForSelector:(SEL)aSelector//可以改變查找方法的對象
3.- (void)forwardInvocation:(NSInvocation *)anInvocation//搭配- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector這個使用。通過傳遞過來的NSInvocation對象,我們可以對此重新注入調(diào)用對象、方法、參數(shù)等

24、NSObject的description和debugDescription方法
當我們在工程中使用model(繼承NSObject)的時候,在斷點或者log調(diào)試中有時候需要將對象打印到控制臺中,但是我們自己寫的model打印出的是對象的地址。如果需要打印出有效的信息,這時我們可以重寫descrition或者debugDescription方法,打印出我們想要的信息。例如對象的所有屬性的值。默認情況下debugDescription調(diào)用的是description方法

25、 UIView的setNeedsLayout、layoutIfNeeded 、setNeedsDisplay

  • setNeedsLayout 標記為需要重新布局,不立即刷新,但layoutSubviews一定會被調(diào)用
    配合layoutIfNeeded立即更新
  • layoutIfNeeded 如果有有需要刷新的標記,立即調(diào)用layoutSubviews進行布局,并且進行頁面刷新
    例如我在使用約束做動畫的時候:
leftContrain.constant = 100
UIView.animateWithDuration(0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.AllowAnimatedContent, animations: {
                self.view.layoutIfNeeded() //立即實現(xiàn)布局
            }, completion: nil)

如果不調(diào)用layoutIfNeeded則動畫一點卵用沒有

  • setNeedsDisplay或setNeedsDisplayInRect 會調(diào)用自動調(diào)用drawRect方法,這樣可以拿到 UIGraphicsGetCurrentContext,就可以畫畫了。而setNeedsLayout會默認調(diào)用layoutSubViews,
    • setNeedsDisplay 對應(yīng)畫布的大小就是UIView的大小,所以會將整個UIView進行繪制
    • setNeedsDisplayInRect 對應(yīng)畫布的大小是調(diào)用此方法傳入的rect,如果此前UIView已經(jīng)繪制過一次,則會保留上次的繪制,僅僅對對應(yīng)傳入的rect進行刷新繪制

      This method makes a note of the request and returns immediately. The view is not actually redrawn until the next drawing cycle, at which point all invalidated views are updated.

setNeedsDisplay方便繪圖,而layoutSubViews方便出來數(shù)據(jù)。

26、Cocoapods導(dǎo)入第三方包Xcode沒有自動補全提示功能
選擇Target -> Build Settings 菜單,找到\”User Header Search Paths\”設(shè)置項
新增一個值"${SRCROOT}",并且選擇\”Recursive\”,這樣xcode就會在項目目錄中遞歸搜索文件

27、通知的命名的正確姿勢

[Name of associated class]+[Did|Will]+[UniquePartOfName]+Notification

我們可以按照蘋果模板寫

.h文件

UIKIT_EXTERN NSNotification const FOFAnimalDidBecomePersonNotification

.m文件

NSNotification FOFAnimalDidBecomePersonNotificationFOFAnimalDidBecomePersonNotification = @"FOFAnimalDidBecomePersonNotification";

28、對象、類、元類、isa

類對象代表類,Class類型。如[類名 class];

所有類的實例都由類對象生成,類對象會把實例的isa的值修改成自己的地址,每個實例的isa都指向該實例的類對象

因為類也是一個對象,那它也必須是另一個類的實例,這個類就是元類 metaclass

元類保存了類方法(靜態(tài)方法"+")的列表。當一個類方法被調(diào)用時,元類會首先查找它本身是否有該類方法的實現(xiàn),如果沒有則該元類會向它的父類查找該方法,直到一直找到繼承鏈的頭。

元類(metaclass)也是一個對象,那么元類的isa指針又指向哪里呢?為了設(shè)計上的完整,所有的元類的isa指針都會指向一個根元類(root metaclass)。

圖.png
最后編輯于
?著作權(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ù)。

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