ios 筆記

1.單例的寫法

  • (instancetype)sharedInstance {
    static id sharedInstance = nil;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    sharedInstance = [[self alloc] init];
    });

    return sharedInstance;
    }
    2.規(guī)范寫法:
    @property (weak, nonatomic) id<SGOAnalyticsDelegate> analyticsDelegate;

@property (assign, nonatomic) NSInteger statusCode;
@synthesize statusCode = _statusCode;

3.GCD 延時操作
dispatch_time_t timer = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_after(timer, dispatch_get_main_queue(), ^{

});

4.設(shè)置父視圖的透明度,添加的子視圖不顯示解決方法 設(shè)置父視圖的顏色:
_bottomView.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.5];

5.計算時間
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[finance.buildDate longLongValue] / 1000];

NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSString *strDate = [dateFormatter stringFromDate:date];

6椭坚、NSString為何要用copy身坐?而不是strong前酿?

我這樣說你就明白了 A->B A中的一個MutableString給B中的一個Property(NSString類型)賦值 首先是能接受的,父類可以接受子類么鹤,如果是retain偎谁,僅僅是生成一個指針,計數(shù)器加一纯蛾,然后指向那個MutableString。如果MString改變纵隔,B中那個跟著改變翻诉,因為是同一塊內(nèi)存區(qū)域帆卓。而選擇Copy相當(dāng)于又生成了一個NSString,與A中的MutableString獨立米丘。

NSArray 和 NADictionary同理

7、對象回收時Weak指針自動被置為nil的實現(xiàn)原理
Runtime維護(hù)了一個Weak表糊啡,用于存儲指向某個對象的所有Weak指針拄查。Weak表其實是一個哈希表,Key是所指對象的地址棚蓄,Value是Weak指針的地址(這個地址的值是所指對象的地址)的數(shù)組堕扶。
在對象被回收的時候,經(jīng)過層層調(diào)用梭依,會最終觸發(fā)下面的方法將所有Weak指針的值設(shè)為nil稍算。

8、響應(yīng)者鏈
事件的傳遞和響應(yīng)的區(qū)別:
事件的傳遞是從上到下(父控件到子控件)役拴,事件的響應(yīng)是從下到上(順著響應(yīng)者鏈條向上傳遞:子控件到父控件糊探。
通過hitTest:withEvent:層層找到最佳響應(yīng)的視圖,然后通過touchesBegan:withEvent:響應(yīng)事件河闰,如果不能響應(yīng)再找父視圖然后從下到上 直到UIApplication如果不能響應(yīng)則廢棄該事件科平。
http://www.reibang.com/p/c294d1bd963d
傳遞過程詳解:
keyWindow會在它的內(nèi)容視圖上調(diào)用hitTest:withEvent:(該方法返回的就是處理此觸摸事件的最合適view)來完成這個找尋過程。
hitTest:withEvent:在內(nèi)部首先會判斷該視圖是否能響應(yīng)觸摸事件姜性,如果不能響應(yīng)瞪慧,返回nil,表示該視圖不響應(yīng)此觸摸事件部念。然后再調(diào)用pointInside:withEvent:(該方法用來判斷點擊事件發(fā)生的位置是否處于當(dāng)前視圖范圍內(nèi))弃酌。如果pointInside:withEvent:返回NO,那么hiteTest:withEvent:也直接返回nil儡炼。
如果pointInside:withEvent:返回YES妓湘,則向當(dāng)前視圖的所有子視圖發(fā)送hitTest:withEvent:消息,所有子視圖的遍歷順序是從最頂層視圖一直到到最底層視圖射赛,即從subviews數(shù)組的末尾向前遍歷多柑。直到有子視圖返回非空對象或者全部子視圖遍歷完畢;若第一次有子視圖返回非空對象楣责,則 hitTest:withEvent:方法返回此對象竣灌,處理結(jié)束;如所有子視圖都返回非秆麸,則hitTest:withEvent:方法返回該視圖自身初嘹。
hitTest:底層實現(xiàn)
// point是該視圖的坐標(biāo)系上的點

  • (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
    {
    // 1.判斷自己能否接收觸摸事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
    // 2.判斷觸摸點在不在自己范圍內(nèi)
    if (![self pointInside:point withEvent:event]) return nil;
    // 3.從后往前遍歷自己的子控件,看是否有子控件更適合響應(yīng)此事件
    int count = self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
    UIView *childView = self.subviews[i];
    CGPoint childPoint = [self convertPoint:point toView:childView];
    UIView *fitView = [childView hitTest:childPoint withEvent:event];
    if (fitView) {
    return fitView;
    }
    }
    // 沒有找到比自己更合適的view
    return self;
    }
    http://www.reibang.com/p/2f664e71c527
    http://www.cocoachina.com/ios/20160630/16868.html

9沮趣、結(jié)合響應(yīng)者鏈擴大button的點擊范圍 重寫pointInside: withEvent:方法 給button添加一個范圍

import <UIKit/UIKit.h>

@interface UIButton (test)
@property(nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end

import "UIButton+test.h"

import <objc/runtime.h>

@implementation UIButton (test)
@dynamic hitTestEdgeInsets;

static const NSString *KEY_HIT_TEST_EDGE_INSETS = @"HitTestEdgeInsets";

-(void)setHitTestEdgeInsets:(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = [NSValue value:&hitTestEdgeInsets withObjCType:@encode(UIEdgeInsets)];
objc_setAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(UIEdgeInsets)hitTestEdgeInsets {
NSValue *value = objc_getAssociatedObject(self, &KEY_HIT_TEST_EDGE_INSETS);
if(value) {
UIEdgeInsets edgeInsets;
[value getValue:&edgeInsets];
return edgeInsets;
}else {
return UIEdgeInsetsZero;
}
}

  • (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    if(UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero) || !self.enabled || self.hidden) {
    return [super pointInside:point withEvent:event];
    }
    CGRect relativeFrame = self.bounds;
    CGRect hitFrame = UIEdgeInsetsInsetRect(relativeFrame, self.hitTestEdgeInsets);
    //判斷這個點是否在這個范圍內(nèi)
    return CGRectContainsPoint(hitFrame, point);
    }
    @end

10屯烦、面試題
http://www.cocoachina.com/programmer/20151019/13746.html

11、觀察者模式 ()
http://www.jb51.net/article/76122.htm
http://www.cnblogs.com/goodboy-heyang/p/5265675.html
kvo: 一對多, 觀察者模式, 是鍵值監(jiān)聽,鍵值觀察機制, KVO的本質(zhì)是當(dāng)觀察者為一個對象的屬性進(jìn)行了注冊,被觀察對象的isa指針被修改的時候驻龟,isa指針就會指向一個中間類温眉,而不是真實的類。所以 isa指針其實不需要指向?qū)嵗龑ο笳鎸嵉念愇毯K晕覀兊某绦蜃詈貌灰蕾囉趇sa指針咳焚。在調(diào)用類的方法的時候管闷,最好要明確對象實例的類名
當(dāng)你觀察一個對象時,會動態(tài)的創(chuàng)建該對象類的子類,這個子類重寫了被觀察屬性的 setter 方法碘赖,同時將該對象的 isa 指針指向了新創(chuàng)建的子類介却。在 Objective-C 中對象是通過 isa 指針來查找對應(yīng)類中的方法列表的童叠,所以這里可以把該對象看為新子類的實例對象结闸。重寫的 setter 方法會在調(diào)用原 setter 方法之后,通知觀察者對象屬性值的更改坎弯。
addObserver:forKeyPath:options:context:
observeValueForKeyPath:ofObject:change:context

當(dāng)某個類的對象第一次被觀察時纺涤,系統(tǒng)就會在運行期動態(tài)地創(chuàng)建該類的一個派生類,在這個派生類中重寫基類中任何被觀察屬性的 setter 方法抠忘。
派生類在被重寫的 setter 方法中實現(xiàn)真正的通知機制洒琢,就如前面手動實現(xiàn)鍵值觀察那樣。這么做是基于設(shè)置屬性會調(diào)用 setter 方法褐桌,而通過重寫就獲得了 KVO 需要的通知機制衰抑。當(dāng)然前提是要通過遵循 KVO 的屬性設(shè)置方式來變更屬性值,如果僅是直接修改屬性對應(yīng)的成員變量荧嵌,是無法實現(xiàn) KVO 的呛踊。
同時派生類還重寫了 class 方法以“欺騙”外部調(diào)用者它就是起初的那個類。然后系統(tǒng)將這個對象的 isa 指針指向這個新誕生的派生類啦撮,因此這個對象就成為該派生類的對象了谭网,因而在該對象上對 setter 的調(diào)用就會調(diào)用重寫的 setter,從而激活鍵值通知機制赃春。此外愉择,派生類還重寫了 dealloc 方法來釋放資源。
//自定義kvo
@implementation NSObject (WSG)

  • (void)wsg_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context{

    //1织中、自定義子類
    //2锥涕、給子類重寫setName方法 調(diào)用父類的setName方法,通知觀察者
    //3狭吼、修改當(dāng)前對象的isa指針层坠,指向自定義的子類
    //Person類
    NSString *oldClass = NSStringFromClass([self class]);
    //新類
    NSString *newClass = [@"wsgKVO_" stringByAppendingString:oldClass];
    //創(chuàng)建的子類對象
    Class myClass = objc_allocateClassPair([self class], [newClass UTF8String], 0);
    //注冊類
    objc_registerClassPair(myClass);

    //給類添加setName方法
    class_addMethod(myClass, @selector(setName:), (IMP)setName, "v@:@");

    //修改isa指針 指向新建的子類
    object_setClass(self, myClass);

    //保存觀察者對象
    objc_setAssociatedObject(self, @"objc", observer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    //保存keypath
    objc_setAssociatedObject(self, @"wsgkeypath", keyPath, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

//調(diào)用父類的setName方法 通知觀察者 observer
void setName(id self,SEL _cmd,NSString *newName){
//調(diào)用父類的set方法 變量是一個結(jié)構(gòu)體 比較負(fù)責(zé)
//objc_msgSendSuper()

//改用 修改self的isa指針指向父類 調(diào)用setName方法  然后通知觀察者 值發(fā)生了改變  然后再改回self的isa指針指向 子類
id class = [self class];
//修改self的isa指向 父類
object_setClass(self, class_getSuperclass(class));
//調(diào)用父類的setName方法 /* id self, SEL op, ... */ 要編譯通過  要去build-setting 搜索objc_msg 設(shè)置enable為NO
objc_msgSend(self,@selector(setName:),newName);

//通知observer 值發(fā)生了改變
id objc = objc_getAssociatedObject(self, @"objc");
id keypath = objc_getAssociatedObject(self, @"wsgkeypath");
objc_msgSend(objc, @selector(observeValueForKeyPath:ofObject:change:context:),self,keypath,nil,nil);

//把self的isa指針改為指向子類類型
object_setClass(self, class);

}
@end

KVC(Key-Value-Coding)內(nèi)部的實現(xiàn):是鍵值編碼, 一個對象在調(diào)用setValue的時候,(1)首先根據(jù)方法名找到運行方法的時候所需要的環(huán) 境參數(shù)刁笙。(2)他會從自己isa指針結(jié)合環(huán)境參數(shù)破花,找到具體的方法實現(xiàn)的接口谦趣。(3)再直接查找得來的具體的方法實現(xiàn)。

Delegate: 通常發(fā)送者和接收者的關(guān)系是直接的一對一的關(guān)系座每。代理的目的是改變或傳遞控制鏈前鹅。允許一個類在某些特定時刻通知到其他類,而不需要獲取到那些類的指針峭梳〉站溃可以減少框架復(fù)雜度。消息的發(fā)送者(sender)告知接收者(receiver)某個事件將要發(fā)生延赌,delegate同意然然后發(fā)送者響應(yīng)事件,delegate機制使得接收者可以改變發(fā)送者的行為叉橱。

Notification: 觀察者模式, 通常發(fā)送者和接收者的關(guān)系是間接的多對多關(guān)系挫以。 消息的發(fā)送者告知接收者事件已經(jīng)發(fā)生或者將要發(fā)送,僅此而已窃祝,接收者并不能反過來影響發(fā)送者的行為掐松。

1). 效率肯定是delegate比NSNotification高。

2). delegate方法比notification更加直接粪小,最典型的特征是大磺,delegate方法往往需要關(guān)注返回值,也就是delegate方法的結(jié)果探膊。比如-windowShouldClose:杠愧,需要關(guān)心返回的是yes還是no。所以delegate方法往往包含should這個很傳神的詞逞壁。也就是好比你做我的delegate流济,我會問你我想關(guān)閉窗口你愿意嗎?你需要給我一個答案腌闯,我根據(jù)你的答案來決定如何做下一步绳瘟。相反的,notification最大的特色就是不關(guān)心接受者的態(tài)度姿骏,我只管把通告放出來糖声,你接受不接受就是你的事情,同時我也不關(guān)心結(jié)果分瘦。所以notification往往用did這個詞匯蘸泻,比如NSWindowDidResizeNotification,那么nswindow對象放出這個notification后就什么都不管了也不會等待接受者的反應(yīng)嘲玫。

1)兩個模塊之間聯(lián)系不是很緊密蟋恬,就用notification傳值,例如多線程之間傳值用notificaiton趁冈。

2)delegate只是一種較為簡單的回調(diào)歼争,且主要用在一個模塊中拜马,例如底層功能完成了,需要把一些值傳到上層去沐绒,就事先把上層的函數(shù)通過delegate傳到底層俩莽,然后在底層call這個delegate,它們都在一個模塊中乔遮,完成一個功能扮超,例如說NavgationController 從 B 界面到A 點返回按鈕 (調(diào)用popViewController方法) 可以用delegate比較好。

12蹋肮、delegate為什么用weak
@protocol LYSunDelegate <NSObject>
@end

@interface LYSun : NSObject
@property (nonatomic, weak) id<LYSunDelegate>delegate;
@end

import "LYPerson.h"

import "LYSun.h"

@interface LYPerson()<LYSunDelegate>
/** 強引用dog*/
@property (nonatomic, strong) LYSun *sun;
@end

@implementation LYPerson

  • (instancetype)init
    {
    self = [super init];
    if (self) {
    // 實例化dog
    self.sun = [[LYSun alloc] init];
    // sun的delegate引用self,self的retainCount出刷,取決于delegate修飾,weak:retainCount不變坯辩,strong:retainCount + 1
    self.sun.delegate = self;
    }
    return self;
    }
  • (void)dealloc
    {
    NSLog(@"LYPerson----銷毀");
    }
    @end

// 實例化person, self對person弱引用馁龟,person的retainCount不變
LYPerson *person = [[LYPerson alloc] init];
當(dāng)delegate 用strong時 :當(dāng)viewController不對person引用后想釋放person的時候,發(fā)現(xiàn)這時sun.delegate對person還強引用著呢漆魔,person的retainCount為1坷檩,所以person不會釋放,sun固然也不會釋放改抡,這就是造成循環(huán)引用導(dǎo)致的內(nèi)存泄漏的原因
當(dāng)delegate用weak時:當(dāng)viewController 不對person引用之后矢炼,person的retainCount 為 0 ,當(dāng)然可以釋放啦阿纤,那么person就被釋放了句灌,sun也就被釋放啦

13、retain欠拾、copy涯塔、assign對于get、set方法的影響
//assain修飾的屬性 生成的set get方法
-(void)setAge:(NSInteger)age
{
_age =age;
}
-(NSInteger )age
{
return _age;
}
//retain 修飾的屬性
-(void)setArray:(NSArray *)array
{
if (_array !=array)
{
[_array release];
_array =[array retain];
}
}

-(NSArray*)array{
return [[_array retain]autorelease];
}
//copy修飾的屬性
-(void)setName:(NSString *)name
{
if (_name !=name) {
[_name release];
_name = [name copy];
}
}
-(NSString *)name
{
return [[_name retain]autorelease];
}
14:SEL
http://www.imlifengfeng.com/blog/?p=398
又叫選擇器清蚀,是表示一個方法的selector的指針
本質(zhì)上匕荸,SEL只是一個指向方法的指針(準(zhǔn)確的說,只是一個根據(jù)方法名hash化了的KEY值枷邪,能唯一代表一個方法)榛搔,它的存在只是為了加快方法的查詢速度
IMP 是方法的實現(xiàn) SEL 和IMP是一一對應(yīng)的

15:tableview計算cell高度
1:專門的layout 來計算各個控件的位置:例如YYKit里https://github.com/ibireme/YYKit
在子線程里計算好之后 在主線程刷新 提高刷新效率东揣。
2践惑、autolayout自動適應(yīng)高度;
3嘶卧、全代碼在cell里的layoutSubviews里代碼計算高度尔觉。
http://www.imlifengfeng.com/blog/?p=501
tableview優(yōu)化:

16、coretext繪制文本


coretext_arch.png

core_text_arch_2x.png

CTFramesetter是由CFAttributedString(NSAttributedString)初始化而來芥吟,可以認(rèn)為它是CTFrame的一個Factory侦铜,通過傳入CGPath生成相應(yīng)的CTFrame并使用它進(jìn)行渲染:直接以CTFrame為參數(shù)使用CTFrameDraw繪制或者從CTFrame中獲取CTLine進(jìn)行微調(diào)后使用CTLineDraw進(jìn)行繪制专甩。

一個CTFrame是由一行一行的CLine組成,每個CTLine又會包含若干個CTRun(既字形繪制的最小單元)钉稍,通過相應(yīng)的方法可以獲取到不同位置的CTRun和CTLine涤躲,以實現(xiàn)對不同位置touch事件的響應(yīng)。

http://blog.devtang.com/2015/06/27/using-coretext-1/
http://blog.devtang.com/2015/06/27/using-coretext-2/
http://blog.csdn.net/hopedark/article/details/50174157

17贡未、runloop
runloop只能在一個mode下 執(zhí)行完一個mode 會休眠 等待下一個种樱。
NSRunLoopCommonModes:占位模式 UI&默認(rèn)
UITrackingRunLoopMode:UI模式
NSDefaultRunLoopMode:默認(rèn)模式
每個mode都對應(yīng)著 source、timer俊卤、observer
source:
source0:非系統(tǒng)內(nèi)核
source1:系統(tǒng)內(nèi)核

1202010-b85e86926d49b75b.png

https://blog.ibireme.com/2015/05/18/runloop/
https://mp.weixin.qq.com/s/O00rVlHgJZ62Nf3QXtp5uA
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(lickedButtonAtIndex) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
runloop:在APP的應(yīng)用:
1嫩挤、NSTimer、CADisplayLink消恍、NSObject(NSDelaydPerforming)
三個都是對CFRunLoopTimer的封裝
2岂昭、UIEvent
3、autorelease
5哺哼、NSObject(NSThreadPerformAddtion)
7、CATransition
8叼风、CAAnimation
9取董、dispatch_get_main_queue()
10、AFNetworking(NSURLConnection)

18无宿、GCD 多線程
隊列:
并行隊列(Concurrent Dispatch Queue):可以讓多個任務(wù)并行(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))并行功能只有在異步(dispatch_async)函數(shù)下才有效
串行隊列(Serial Dispatch Queue):讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后茵汰,再執(zhí)行下一個任務(wù))
任務(wù):
同步執(zhí)行(sync):只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
異步執(zhí)行(async):可以在新的線程中執(zhí)行任務(wù)孽鸡,具備開啟新線程的能力

//并行隊列
dispatch_queue_t queue = dispatch_queue_create("com.sunny.test", DISPATCH_QUEUE_CONCURRENT);
//串行隊列
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_SERIAL);
//全局并行隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//全局隊列的優(yōu)先級
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND

創(chuàng)建任務(wù):
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼
});


屏幕快照 2017-11-02 下午3.30.39.png

主隊列+同步執(zhí)行 會阻塞主線程 導(dǎo)致死鎖

// 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"2-------%@",[NSThread currentThread]);
});

//延時操作
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
NSLog(@"run-----");
});

//只執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});

dispatch_barrier_async
需要異步執(zhí)行兩組操作蹂午,而且第一組操作執(zhí)行完之后,才能開始執(zhí)行第二組操作
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
    NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----2-----%@", [NSThread currentThread]);
});

執(zhí)行完上面兩個任務(wù)之后 再執(zhí)行下面兩個
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});

dispatch_async(queue, ^{
    NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"----4-----%@", [NSThread currentThread]);
});

隊列組
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后彬碱,回到主線程...
});

//信號量
dispatch_semaphore
//創(chuàng)建信號量
dispatch_semaphore_create
//發(fā)送信號量
dispatch_semaphore_signal
//等待信號量
dispatch_semaphore_wait

//創(chuàng)建一個并行隊列
dispatch_queue_t queque = dispatch_queue_create("GoyakodCreated", DISPATCH_QUEUE_CONCURRENT);
//異步執(zhí)行
dispatch_async(queque, ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self getToken:semaphore];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self request];
});
收到信號量之后 才執(zhí)行接下來的任務(wù)
在getToken方法里有發(fā)送信號量方法:dispatch_semaphore_signal(semaphore);
然后會往下執(zhí)行request方法
//計時 比NSTImer更精準(zhǔn)
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1000000000, 0);
dispatch_source_set_event_handler(self.timer, ^{
NSLog(@"---------%@",[NSThread currentThread]);
});
dispatch_resume(self.timer);

19豆胸、NSString內(nèi)存分配
方法1.直接賦值: NSString *testStr1 = @"a";
方法2.類函數(shù)初始化生成:
NSString *testStr2 = [NSString stringWithString:@"b"];
NSString *testStr3 = [NSString stringWithFormat:@"c"];
方法3.實例方法初始化生成: 
NSString *testStr4 = [[NSString alloc] initWithString:@"d"];
NSString *testStr5 = [[NSString alloc] initWithFormat:@"e"];
test1,test2,test4都是在一個內(nèi)存區(qū)域,也就是上文所說的常量內(nèi)存區(qū)巷疼。test3,test5在一個內(nèi)存區(qū)晚胡,也就是堆區(qū)
test1,test2,test4這三種的話建議用=@“字符串”來使用,因為本來就是一樣的嚼沿。test3,test5這兩種的話估盘,建議用texst3這種

20、dsYM文件 分析
xcode 打包 archive 之后 會生成文件. xcarchive 路徑(~/Library/Developer/Xcode/Archives)
每一個xx.app 和 xx.app.dSYM都有對應(yīng)的UUID骡尽,crash文件也有自己的UUID遣妥,只要這三個文件的UUID一致,通過命令行解析出錯誤的函數(shù)地址攀细,
查看xx.app.dSYM 的UUID
dwarfdump --uuid xx.app.dSYM
查看crash地址對應(yīng)的函數(shù)
atos -arch arm64 -o Zeus -l 0x10063000 0x00000001009795d4

21箫踩、HMAC加密 替代MD5加密 MD5加密的加鹽加密 在APP中存在危險 如果鹽泄露 將導(dǎo)致整個加密流程收到威脅 修改鹽 影響所有用戶爱态。
// HMACMD5加密方法 每個用戶有一個key 從服務(wù)端獲取 可以更新key

  • (NSString *)hmacMD5StringWithKey:(NSString *)key {
    const char *keyData = key.UTF8String;
    const char *strData = self.UTF8String;
    uint8_t buffer[CC_MD5_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgMD5, keyData, strlen(keyData), strData, strlen(strData), buffer);
    return [self stringFromBytes:buffer length:CC_MD5_DIGEST_LENGTH];
    }
    HMAC加密之后 再把密碼+201711302229 md5加密 到服務(wù)器校驗 服務(wù)器也是把HMAC密碼加上時間(到分) 跟APP傳過來的進(jìn)行匹配 。

22班套、成員變量 屬性
@interface Person : NSObject{
@public
NSString *_name;//成員變量
}
@property (nonatomic,copy) NSString *age;//屬性
@end
Person *p = [[Person alloc] init];
p->_name = @"sunny";//實例變量賦值采用箭頭 kvo觀察不到值得變化
p.age = @"18";//實例變量賦值采用set方法 kvo可以觀察到值得變化

23肢藐、遞歸沒有return 會棧溢出 調(diào)用函數(shù) 就會分配棧空間 沒有return 會一直分配椫ň拢空間 吆豹。

24、2理盆、在iOS 11上運行tableView向下偏移64px或者20px痘煤,因為iOS 11廢棄了automaticallyAdjustsScrollViewInsets,而是給UIScrollView增加了contentInsetAdjustmentBehavior屬性猿规。避免這個坑的方法是要判斷
if (@available(iOS 11.0, *)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}else {
self.automaticallyAdjustsScrollViewInsets = NO;
}

25衷快、設(shè)置導(dǎo)航欄為透明
self.navigationController.navigationBar.translucent = YES;
self.navigationController.navigationBar.barTintColor = clear_color;
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:[UIImage new]];

26、設(shè)置導(dǎo)航欄為不透明
self.navigationController.navigationBar.translucent = NO;
self.navigationController.navigationBar.barTintColor = RGBCOLOR(64, 98, 153);
[self.navigationController.navigationBar setBackgroundImage:nil forBarMetrics:UIBarMetricsDefault];
[self.navigationController.navigationBar setShadowImage:nil];

27姨俩、CFBundleURLType:當(dāng)前APP的scheme記錄蘸拔, 通過schema可在其它App中打開當(dāng)前App
LSApplicationQueriesSchemes是從iOS9時被引入的。
用意:當(dāng)前APP允許訪問的APP有哪些环葵,即白名單调窍,需要通信雙方 均設(shè)置為對方的scheme,否則當(dāng)調(diào)用對方App時张遭,系統(tǒng)會告訴你This app is not allowed to query for scheme邓萨。
調(diào)用者和被調(diào)用者均需要設(shè)置白名單,一方想調(diào)用菊卷,另一方需要也知道將被你調(diào)用 缔恳,更為安全

28、Block https://halfrost.com/ios_block/?utm_source=tuicool
帶有自動變量(局部變量)的匿名函數(shù)
Block捕獲外部變量
靜態(tài)全局變量洁闰、全局變量:作用域在全局可以訪問
靜態(tài)變量:靜態(tài)變量傳遞給Block是內(nèi)存地址值歉甚,所以能在Block里面直接改變值
局部變量:在執(zhí)行Block語法的時候,Block語法表達(dá)式所使用的自動變量的值是被保存進(jìn)了Block的結(jié)構(gòu)體實例中扑眉,也就是Block自身中铃芦,Block僅僅捕獲了值,并沒有捕獲內(nèi)存地址

Block中改變變量值有2種方式襟雷,一是傳遞內(nèi)存地址指針到Block中刃滓,二是改變存儲區(qū)方式(__block)

帶有 __block的變量也被轉(zhuǎn)化成了一個結(jié)構(gòu)體__Block_byref_i_0
ARC環(huán)境下,Block捕獲外部對象變量耸弄,是都會copy一份的咧虎,地址都不同。只不過帶有__block修飾符的變量會被捕獲到Block內(nèi)部持有

1.手動調(diào)用copy
2.Block是函數(shù)的返回值
3.Block被強引用计呈,Block被賦值給__strong或者id類型
4.調(diào)用系統(tǒng)API入?yún)⒅泻衭singBlcok的方法
系統(tǒng)都會默認(rèn)調(diào)用copy方法把Block賦復(fù)制

29砰诵、+load 在把類加載到內(nèi)存的時候運行的方法
load是只要類所在文件被引用就會被調(diào)用征唬,而initialize是在類或者其子類的第一個方法被調(diào)用前調(diào)用。所以如果類沒有被引用進(jìn)項目茁彭,就不會有l(wèi)oad調(diào)用总寒;但即使類文件被引用進(jìn)來,但是沒有使用理肺,那么initialize也不會被調(diào)用摄闸。

30、消息轉(zhuǎn)發(fā)機制
1妹萨、方法調(diào)用[receiver message]
2年枕、編譯之后objc_msgSend(receiver, selector)
3、通過receiver的isa指針找到類對象乎完,首先從類對象的緩存中去找方法找到則執(zhí)行熏兄,如果沒有找到則去方法列表查找,如果沒有找到則去父類的方法緩存列表查找

方法的結(jié)構(gòu)體實現(xiàn):
struct objc_method {
SEL method_name 方法名 OBJC2_UNAVAILABLE;
char *method_types 方法的參數(shù) OBJC2_UNAVAILABLE;
IMP method_imp 方法的實現(xiàn) OBJC2_UNAVAILABLE;
}
IMP的定義

if !OBJC_OLD_DISPATCH_PROTOTYPES

typedef void (IMP)(void / id, SEL, ... */ );

else

typedef id (*IMP)(id, SEL, ...);

endif

類方法:保存在元類里树姨,類對象的isa指向元類

如果找不到實例方法:在Objective中摩桶,對一個對象發(fā)送一個它沒有實現(xiàn)的Selector是完全合法的,這樣做可以隱藏某一個消息背后實現(xiàn)帽揪,也可以模擬多繼承(OC不支持多繼承)硝清。這個機制就是動態(tài)轉(zhuǎn)發(fā)機制

+resolveInstanceMethod:

  • forwardingTargetForSelector:
  • forwardInvocation:

動態(tài)方法的機制第一步,類自己處理
使用resolveInstanceMethod
動態(tài)為實例方法提供一個實現(xiàn)
這個方法在Objective C消息轉(zhuǎn)發(fā)機制之前被調(diào)用台丛。如果 respondsToSelector或者instancesRespondToSelector: 被調(diào)用,可以為改Selector提供動態(tài)的實現(xiàn)者耍缴。

第二步(第一步不能處理的情況下)砾肺,調(diào)用forwardingTargetForSelector來簡單的把執(zhí)行任務(wù)轉(zhuǎn)發(fā)給另一個對象挽霉,到這里,還是廉價調(diào)用
-(id)forwardingTargetForSelector:(SEL)aSelector{

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wundeclared-selector"

if (aSelector == @selector(dynamicSelector) && [對象 respondsToSelector:@selector(dynamicSelector)]) {
    return self.myObj;
}else{
    return [super forwardingTargetForSelector:aSelector];
}

pragma clang diagnostic pop

}
第三步变汪,當(dāng)前兩步都不能處理的時候侠坎,調(diào)用forwardInvocation轉(zhuǎn)發(fā)給別人,返回值仍然返回給最初的Selector
-(void)forwardInvocation:(NSInvocation *)anInvocation{
if ([self.myObj respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:對象];
}else{
[super forwardInvocation:anInvocation];
}
}

  • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [CustomObject instanceMethodSignatureForSelector:aSelector];
    }

31裙盾、weak实胸、assign 都不會使引用計數(shù)+1
assign通常修飾基本數(shù)據(jù)類型,weak修飾對象番官,會在對象釋放后置為nil庐完,但assign不會,

32徘熔、有關(guān)于對于AFN的二次封裝
可以取消單個任務(wù)(存儲每個任務(wù)的taskIdentifier门躯,然后根據(jù)這個刪除任務(wù))
首先做一個單例做各種請求,單例有一個AFURLSessionManager或者AFHTTPSessionManager,來控制任務(wù)的執(zhí)行酷师,每執(zhí)行一個任務(wù) 把任務(wù)放到一個字典里 任務(wù)的taskIdentifier作為key讶凉,任務(wù)為value
然后做一個請求類:配置封裝請求參數(shù) 調(diào)用單例請求數(shù)據(jù)

33染乌、靜態(tài)庫、動態(tài)庫
靜態(tài)庫:鏈接時完整地拷貝至可執(zhí)行文件中懂讯,被多次使用就有多份冗余拷貝荷憋。
動態(tài)庫:鏈接時不復(fù)制,程序運行時由系統(tǒng)動態(tài)加載到內(nèi)存褐望,供程序調(diào)用勒庄,系統(tǒng)只加載一次,多個程序共用譬挚,節(jié)省內(nèi)存
iOS里靜態(tài)庫形式
.a和.framework
iOS里動態(tài)庫形式
.dylib和.framework

系統(tǒng)的.framework是動態(tài)庫锅铅,我們自己建立的.framework是靜態(tài)庫

為什么要使用靜態(tài)庫?
1 方便共享代碼减宣,便于合理使用盐须。
2 實現(xiàn)iOS程序的模塊化∑犭纾可以把固定的業(yè)務(wù)模塊化成靜態(tài)庫贼邓。
3 和別人分享你的代碼庫,但不想讓別人看到你代碼的實現(xiàn)闷尿。
4 開發(fā)第三方sdk的需要塑径。

34、iOS 防止 Charles 抓取數(shù)據(jù)
1填具、通過 HTTP/1.1 及以上版本的 CONNECT 請求方式
2统舀、使用自簽名證書的應(yīng)用和雙向驗證的應(yīng)用:其一,客戶端通過指定的方式只信任某一個證書劳景;其二誉简,一般做法只有客戶端驗證服務(wù)端公鑰證書是不是合法,但是某些 app盟广,比如支付寶闷串,采用雙向驗證的方式,在通信過程中筋量,服務(wù)器會驗證 app 的公鑰證書烹吵,這時候,就沒辦法使用 Charles(中間人攻擊的方式)進(jìn)行抓包桨武。
3肋拔、對返回數(shù)據(jù)進(jìn)行加密(RAS保密 + token驗證 & 效率更高的AES) 。
4呀酸、判斷客戶端當(dāng)前是否設(shè)置了代理凉蜂。這也是本人通過 NSURLProtocol 攔截請求后,判斷是否設(shè)置了代理,實現(xiàn)了防止 Charles 抓取 APP 的數(shù)據(jù)

  • (BOOL)getProxyStatus {
    NSDictionary *proxySettings = (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
    NSDictionary *settings = [proxies objectAtIndex:0];

    NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
    NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
    NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);

    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]){
    //沒有設(shè)置代理
    return NO;
    }else{
    //設(shè)置代理了
    return YES;
    }
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末跃惫,一起剝皮案震驚了整個濱河市叮叹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌爆存,老刑警劉巖蛉顽,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異先较,居然都是意外死亡携冤,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門闲勺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來曾棕,“玉大人,你說我怎么就攤上這事菜循∏痰兀” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵癌幕,是天一觀的道長衙耕。 經(jīng)常有香客問我,道長勺远,這世上最難降的妖魔是什么橙喘? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮胶逢,結(jié)果婚禮上厅瞎,老公的妹妹穿的比我還像新娘。我一直安慰自己初坠,他們只是感情好和簸,可當(dāng)我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著某筐,像睡著了一般比搭。 火紅的嫁衣襯著肌膚如雪冠跷。 梳的紋絲不亂的頭發(fā)上南誊,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音蜜托,去河邊找鬼抄囚。 笑死,一個胖子當(dāng)著我的面吹牛橄务,可吹牛的內(nèi)容都是我干的幔托。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼重挑!你這毒婦竟也來了嗓化?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤谬哀,失蹤者是張志新(化名)和其女友劉穎刺覆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體史煎,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡谦屑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了篇梭。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氢橙。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恬偷,靈堂內(nèi)的尸體忽然破棺而出悍手,到底是詐尸還是另有隱情,我是刑警寧澤袍患,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布谓苟,位于F島的核電站,受9級特大地震影響协怒,放射性物質(zhì)發(fā)生泄漏涝焙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一孕暇、第九天 我趴在偏房一處隱蔽的房頂上張望仑撞。 院中可真熱鬧,春花似錦妖滔、人聲如沸隧哮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沮翔。三九已至,卻和暖如春曲秉,著一層夾襖步出監(jiān)牢的瞬間采蚀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工承二, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留榆鼠,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓亥鸠,卻偏偏與公主長得像妆够,于是被迫代替她去往敵國和親识啦。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,851評論 2 361