?OC的動(dòng)態(tài)性:會(huì)把編譯和鏈接是需要執(zhí)行的邏輯延遲到運(yùn)行時(shí),例如使用 id 所修飾的變量會(huì)在運(yùn)行的時(shí)候才確定具體類型是什么,runtime 的方法交換等。
循環(huán)引用:當(dāng)一個(gè)對(duì)象間接或直接地持有另一個(gè)對(duì)象,而這個(gè)對(duì)象又有強(qiáng)指針指向該對(duì)象,就會(huì)引起循環(huán)應(yīng)用無法釋放的問題。
1.block的使用 使用 weakSelf處理
2.delegate 作為屬性用weak修飾,也可以防止野指針的出現(xiàn)
3.NSTimer 最為成員變量。解決方式
在程序中除了在 dealloc方法去明確使用 invalid 然后賦值為nil
如果無法明確知道什么時(shí)候才可以停止,可以添加了一個(gè)類別讓原來的通過@selector執(zhí)行任務(wù)的方式轉(zhuǎn)換為block的方式。
+ (NSTimer *)xx_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
block:(void(^)())block
repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
target:self
selector:@selector(xx_blockInvoke:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)xx_blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if(block) {
block();
}
}
然后當(dāng)控制釋放的時(shí)候,停止定時(shí)器就可以
-(void)dealloc{
NSLog(@"dealloc");
[self.timer invalidate];
self.timer = nil;
NSLog(@"timer:%@",self.timer);
}
屬性修飾
assign:基本數(shù)據(jù)類型,結(jié)構(gòu)體
weak:delegate(野指針,循環(huán)引用),IB引進(jìn)來的UI控件(視圖層級(jí)結(jié)構(gòu))
copy:不可變類型(指向不同對(duì)象),block(保證是放在堆上)
strong:修飾對(duì)象,強(qiáng)引用
retain:和strong類似,在修飾block的時(shí)候作用相當(dāng)于assign
類別作用:
在保持原有類的情況下,增加其方法(UIScrollView,圖片緩存裁剪處理)
講一個(gè)龐大的類根據(jù)作用分解到不同的類別中,便于維護(hù)
static修飾的變量并不會(huì)改變作用范圍,改變的只是內(nèi)存分配方式(存放在靜態(tài)區(qū)在整個(gè)運(yùn)行期間一直存在)
數(shù)據(jù)持久化:原理都是數(shù)據(jù)保存到本地文件中
1.屬性列表:一般使用NSUserDefaults操作(沙盒l(wèi)ibrary/preference)
2.對(duì)象歸檔:將對(duì)象轉(zhuǎn)化為NSData寫入帶本地文件,對(duì)象需要實(shí)現(xiàn)NSCoding協(xié)議
[NSKeyedArchiver archiveRootObject:self toFile:fileName];
3.SQLite3:FMDB封裝庫
4.Core Data:對(duì)SQLite基于面向?qū)ο蟮姆庋b
棧區(qū)(stack)由編譯器自動(dòng)分配釋放,存放方法(函數(shù))的參數(shù)值,局部變量的值等,是一塊連續(xù)的內(nèi)存的區(qū)域。即棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的。
堆區(qū)(heap)一般由程序員分配釋放,是不連續(xù)的內(nèi)存區(qū)域,從而堆獲得的空間比較靈活。有我們?nèi)ew/alloc來創(chuàng)建的對(duì)象,就是放在堆下面。
棧區(qū)(stack)由編譯器自動(dòng)分配釋放,存放方法(函數(shù))的參數(shù)值,局部變量的值等,是一塊連續(xù)的內(nèi)存的區(qū)域。即棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預(yù)先規(guī)定好的。
堆區(qū)(heap)一般由程序員分配釋放,是不連續(xù)的內(nèi)存區(qū)域,從而堆獲得的空間比較靈活。有我們?nèi)ew/alloc來創(chuàng)建的對(duì)象,就是放在堆下面。
結(jié)構(gòu)體:
struct CGPoint {
CGFloat x;
CGFloat y;
};
typedef struct CGPoint CGPoint;
TableViewCell \ 頁面優(yōu)化:
1.盡量減少透明圖層的使用來減少渲染次數(shù),opaque = YES
2.減少對(duì)象的創(chuàng)建或者使用更為輕的對(duì)象,如當(dāng)視圖元素不需要響應(yīng)用戶的觸摸事件使用CALayer代替UIView
3.當(dāng)大量文字需要顯示,cpu需要耗費(fèi)比較多的資源去進(jìn)行渲染和排版,所以這個(gè)時(shí)候可以選擇CoreText 來處理
4.避免大圖使用,程序解碼比較耗時(shí)
5.復(fù)雜布局使用autoLayout會(huì)影響性能,更好的是選擇手動(dòng)布局
6.使用CoreGraphics進(jìn)行繪制的時(shí)候,如果繪制比較耗時(shí),可以異步處理后再返回主線程顯示
7.TableViewCell的重用,避免在heightForCell方法進(jìn)行較多的處理,將動(dòng)態(tài)高度的緩存
7.離屏渲染(系統(tǒng)會(huì)在屏幕緩沖外開辟新的緩沖去處理),設(shè)置shadow時(shí)候不設(shè)置shadowPath,在設(shè)置圓角不同時(shí)使用 maskToBound和cornerRdius有時(shí)會(huì)導(dǎo)致,或者使用CAShapeLayer 和BezierPath結(jié)合的的方式繪制圓角。
7.如果不需要要顯示透明圖層,那么將試圖的opaque設(shè)置為YES(默認(rèn)),會(huì)讓系統(tǒng)會(huì)將試圖作為不透明去處理而加快渲染。
但如果需要使用到透明,則設(shè)置為NO。
8.減少視圖的層級(jí)結(jié)構(gòu)
4.可以根據(jù)需要是使用預(yù)加載或者延遲加載。
8.使用Instrument工具去檢測
線程加鎖 : 解決多線程同時(shí)訪問同一資源導(dǎo)致的問題,一般通過加鎖處理或者使用串行隊(duì)列處理:
//串行隊(duì)列
dispatch_queue_create("", DISPATCH_QUEUE_SERIAL), ^ {
}
1.GCD信號(hào)量
+(NSString*)getContacts{
//獲取通訊錄權(quán)限
ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();
if(authStatus != kABAuthorizationStatusAuthorized)
{
//是否有通訊錄權(quán)限
__block BOOL accessGranted = NO;
ABAddressBookRef tmpAddressBook = ABAddressBookCreateWithOptions(NULL,NULL);
dispatch_semaphore_t sema=dispatch_semaphore_create(0);//創(chuàng)建信號(hào)量 為0
ABAddressBookRequestAccessWithCompletion(tmpAddressBook, ^(bool granted,CFErrorRef error){
accessGranted = granted;
dispatch_semaphore_signal(sema);//信號(hào)量 +1
});
//當(dāng)信號(hào)量 > 0 才繼續(xù)運(yùn)行,并且信號(hào)量 - 1
dispatch_semaphore_wait( sema, DISPATCH_TIME_FOREVER);
if(accessGranted ==NO){
return @"[]";
}
}
//讀取聯(lián)系人----------此處省略聯(lián)系人讀取步驟-------------------}
2.NSLock *lock = [NSLock alloc]init];
[lock lock];
.....
[lock unlock];
3.條件鎖(基于信號(hào)量)
NSConditionLock *conditionLock = nil;
BOOL canLock = [conditionLock tryLockWhenCondition:kCondition_A];
if (canLock) {
FDLog(@"線程A lock, 請(qǐng)等待");
[conditionLock unlock];
}else{
FDLog(@"線程A 條件不滿足,未加lock");
}
4.NSRecursiveLock 遞歸鎖,同一個(gè)線程可以多次加鎖,但是不會(huì)引起死鎖,如果是NSLock,則會(huì)導(dǎo)致崩潰
- (void)reverseDebug:(NSUInteger )num lock:(NSRecursiveLock *)lock
{
? ? ?[lock lock];
? ? if (num<=0) {
? ? ? ? ?FDLog(@"結(jié)束");
? ? ? ? ?return;
? ? }
? ? [self reverseDebug:num-1 lock:lock];//遞歸
? ? [lock unlock];//還是需要解鎖
}
5.@synchronized(self) { ? ? ? ?
? ? ? ...
}
6.atomic的線程安全只限于setter和getter方法。會(huì)在setter方法里面加鎖。
當(dāng)我們?nèi)?dòng) RunLoop,系統(tǒng)會(huì)自動(dòng)在內(nèi)部創(chuàng)建自動(dòng)釋放池。
數(shù)組遍歷
當(dāng)使用block的方式進(jìn)行遍歷,會(huì)阻塞線程,直到遍歷結(jié)束。
UIView的 drawRect,layoutSubviews 方法調(diào)用時(shí)機(jī):
兩者都是在視圖將要顯示(viewWillAppear之后,viewDidAppear之前調(diào)用,layoutSubviews只要addSubviews就可以調(diào)用,但是drawRect方法必須設(shè)置了frame才能調(diào)用,否則就不會(huì)被調(diào)用)
layoutSubviews
1、init初始化不會(huì)觸發(fā)layoutSubviews。
2、addSubview會(huì)觸發(fā)layoutSubviews
3、設(shè)置view的Frame會(huì)觸發(fā)layoutSubviews,當(dāng)然前提是frame的值設(shè)置前后發(fā)生了變化
4、滾動(dòng)一個(gè)UIScrollView會(huì)觸發(fā)layoutSubviews
5、旋轉(zhuǎn)Screen會(huì)觸發(fā)父UIView上的layoutSubviews事件
6、改變一個(gè)UIView大小的時(shí)候也會(huì)觸發(fā)父UIView上的layoutSubviews事件
7、調(diào)用setNeedsLayout(延時(shí)到下個(gè)更新周期調(diào)用)
drawRect(在調(diào)用前會(huì)先調(diào)用sizeToFit方法,可以再這個(gè)方法計(jì)算坐標(biāo)處理)
1.在UIView初始化時(shí)設(shè)置rect大小,drawRect 就會(huì)自動(dòng)調(diào)用(在視圖將要顯示時(shí)候調(diào)用,只調(diào)用一次,viewDidLoad兩方法之后掉用的),否則不被自動(dòng)調(diào)用。
2.調(diào)用setNeedsDisplay(異步執(zhí)行)
layoutSubviews:UIView 需要對(duì)子控件進(jìn)行手動(dòng)的布局,則可以重寫此方法。只有在autoresizing和constraint-based behaviors of subviews不能提供我們想要的布局結(jié)果的時(shí)候,我們才應(yīng)該重寫此方法。
layoutSubviews方法調(diào)用先于drawRect
MVC優(yōu)點(diǎn):
體現(xiàn)在低藕性與重用性:
數(shù)據(jù)層與視圖層的互相獨(dú)立,可單獨(dú)地對(duì)某一模塊進(jìn)行改變,而不會(huì)影響到其余模塊,也可以根據(jù)實(shí)際需要靈活地重用各個(gè)模塊。
NSOperation與GCD區(qū)別:
GCD是基于C語言的一套api;而NSOperaion底層是基于GCD開發(fā),因此效率方面GCD更好。
而NSOperation更適用于一些復(fù)雜的任務(wù):
1.可以對(duì)隊(duì)列暫停,恢復(fù),取消操作(NSOperationQueue cancelAllOperations setSuspended),不過這里暫停并不是暫停當(dāng)前的任務(wù)而是下一個(gè)任務(wù),恢復(fù)會(huì)從第一個(gè)沒有執(zhí)行的任務(wù)開始。
2.可以建立任務(wù)間的依賴關(guān)系(NSOperation setDependency)
3.NSBlockOperation NSInvocationOperation可以設(shè)置任務(wù)的優(yōu)先級(jí)(setQueuePriority),
而GCD只可以設(shè)置隊(duì)列的優(yōu)先級(jí).
TCP三次握手:
客戶端向服務(wù)端發(fā)送請(qǐng)求 - 服務(wù)端返回?cái)?shù)據(jù)包給客戶端 - 客戶端返回響應(yīng)包給服務(wù)端,連接建立
TCP釋放連接四次握手:
客戶端向服務(wù)端發(fā)送斷開請(qǐng)求 - 服務(wù)端接收到請(qǐng)求后先返回響應(yīng)給客戶端,然后連接斷開后再返回一次數(shù)據(jù)包給客戶端- 客戶端收到服務(wù)端的返回,返回響應(yīng)給服務(wù)端。
9.CoreData多線程安全處理
CoreData包含(實(shí)體對(duì)象,模型對(duì)象,上下文,持久化存儲(chǔ)協(xié)調(diào)器)
線程注意點(diǎn):
1.manageObject,manageObjectContext只能在自己的線程處理,不能跨線程
2.對(duì)于持久化存儲(chǔ)協(xié)調(diào)器(NSPersistentStoreCoordinator)可以多線程共享
3.magageObject的線程間傳遞通過id,并且通過objectWithID來獲取
解決方法:
共同使用一個(gè) NSPersistentStoreCoordinator,以及兩個(gè)獨(dú)立的 Contexts,一個(gè)context 負(fù)責(zé)主線程與UI協(xié)作,一個(gè)context在后臺(tái)負(fù)責(zé)耗時(shí)的處理,用Notifications的方式通知主線程的NSManagedObjectContext進(jìn)行mergeChangesFromContextDidSaveNotification 對(duì)變化進(jìn)行合并處理。
//這個(gè)通知是系統(tǒng)定義的通知
[[NSNotificationCenter defaultCenter] addObserverForName:NSManagedObjectContextDidSaveNotification object:nil queue:[NSOperationQueue currentQueue] usingBlock:^(NSNotification *note) {
if (note.object == self.privateContext) {
dispatch_async(dispatch_get_main_queue(), ^{
//返回主線程合并處理,這里的note.object不能再主線程進(jìn)行使用,需要合并處理
[self.mainContext performBlock:^{
[self.mainContext mergeChangesFromContextDidSaveNotification:note];
}];
});
}
}];
14.網(wǎng)絡(luò)安全:
1.為了防止非法使用api,在每次向服務(wù)器請(qǐng)求的使用,將appKey和加密后的appSecret也發(fā)送給服務(wù)器,服務(wù)器根據(jù)appKey找到對(duì)應(yīng)的appSecret與從客戶端傳遞過來進(jìn)行比對(duì),一致的話就是合法請(qǐng)求。(appkey 和 secret key相當(dāng)于當(dāng)前賬戶的另外一套賬號(hào)和密碼機(jī)制)
2.使用https協(xié)議。
82.常見的crash情況
1.訪問野指針,比如訪問一個(gè)已經(jīng)釋放的對(duì)象的屬性方法(解決方式是當(dāng)一個(gè)指針?biāo)赶虻膶?duì)象如果已被釋放,則將指針置nil)
2.訪問數(shù)組類對(duì)象越界或插入了空對(duì)象
3.訪問了不存在的方法
4.當(dāng)某個(gè)對(duì)象會(huì)被多個(gè)線程修改(加鎖處理)
5.NSNotification KVO的非對(duì)稱添加刪除(訪問野指針)
數(shù)據(jù)庫事務(wù)處理
當(dāng)我們對(duì)數(shù)據(jù)庫進(jìn)行更新操作時(shí),默認(rèn)會(huì)將操作一條條地往數(shù)據(jù)庫提交。而使用事務(wù)處理,則是將所有的操作一次性提交給數(shù)據(jù)庫,當(dāng)處理的過程中出現(xiàn)問題,則可以進(jìn)行回滾操作不進(jìn)行任何修改,保證數(shù)據(jù)的完整性。
所以當(dāng)需要對(duì)大量的數(shù)據(jù)進(jìn)行更新操作時(shí),使用事務(wù)在效率和安全性都會(huì)大大提高。
16.斷點(diǎn)續(xù)傳
//使用HEAD方法,僅獲取目標(biāo)文件的信息,而不做實(shí)際的下載工作。
//[request setHTTPMethod:@"HEAD"];
/**
設(shè)置斷點(diǎn)續(xù)傳的思路:
HeaderField:頭域(請(qǐng)求頭部的字段)
可以通過指定range的范圍逐步地下載指定范圍內(nèi)的數(shù)據(jù),待下載完成后,再將這些數(shù)據(jù)拼接成一個(gè)文件。
1根據(jù)HEAD方法獲取到要下載的文件的總大小、
2在磁盤上建立一個(gè)臨時(shí)的緩沖文件,該文件的大小與目標(biāo)文件大小一致
3緩沖文件中所有字節(jié)都是默認(rèn)為0
4開啟多線程,分別加載不同的range頭指定的數(shù)據(jù)塊,待數(shù)據(jù)塊加載完成以后,將其分別寫入對(duì)應(yīng)的偏移地址。
5所有數(shù)據(jù)下載完成以后,表示文件下載完成,將臨時(shí)文件名更改為目標(biāo)文件。
開發(fā)的難點(diǎn):
0在寫入文件之前,首先要建立一個(gè)同等大小的文件。
1文件的讀寫問題,在oc里默認(rèn)是覆蓋,追加,如果要指定位置,需要用seek方法,移動(dòng)文件指針。
2在多線程寫入文件時(shí),文件的鎖定操作是一個(gè)問題。
*/
[request setValue:@"bytes=0-499" forKeyPath:@"range"];//表di示只讀取數(shù)據(jù)的第0個(gè)字節(jié)到第499個(gè)字節(jié)。
線程間通信:
1.performSelectorOnMainThread,去主線程執(zhí)行
? ?performSelectorInBackground ,后臺(tái)線程執(zhí)行
2.通過NSMachPort端口
將端口添加到當(dāng)前線程的runloop里,通過這個(gè)端口與其他線程進(jìn)行消息的發(fā)送接收(通過delegate 來進(jìn)行消息接受的回調(diào),通過回調(diào)方法傳遞過來的參數(shù),獲取對(duì)方的端口,通過端口就可以發(fā)送消息)。
NSPort *myPort = [NSMachPort port];
//2. 設(shè)置port的代理回調(diào)對(duì)象myPort.delegate=self;
//3. 把port加入runloop,接收port消息
[[NSRunLoop currentRunLoop] addPort:myPort forMode:NSDefaultRunLoopMode];
- (void)handlePortMessage:(NSMessagePort*)message{
//
NSPort*localPort = [message valueForKeyPath:@"localPort"];
NSPort*remotePort = [message valueForKeyPath:@"remotePort"];
//使用端口發(fā)送消息
[remotePort sendBeforeDate:[NSDatedate]
msgid:kMsg2
components:nilfrom:localPort
reserved:0];
}
APP通信方式:
1.URL Scheme:App1通過openURL的方法跳轉(zhuǎn)到App2,并且在URL中帶上想要的參數(shù)(需要在info.plist中配置好URL types),常用于 分享到微信朋友圈微博等功能。
2.Keychain:微信登陸,使用同一個(gè)賬號(hào)平臺(tái),實(shí)現(xiàn)自動(dòng)登錄其他的平臺(tái)。每個(gè)程序都有一個(gè)獨(dú)立的keychain存儲(chǔ),只需要使用對(duì)應(yīng)的登錄SDK,通過共享keychain中的數(shù)據(jù),那么就可以實(shí)現(xiàn)統(tǒng)一賬戶登錄了。(比如密碼、證書等等,就需要使用更為安全的keychain了。keychain里保存的信息不會(huì)因App被刪除而丟失,在用戶重新安裝App后依然有效,數(shù)據(jù)還在。它是一個(gè)sqlite數(shù)據(jù)庫,其保存的所有數(shù)據(jù)都是加密過的。)
3.UIPasteboard(剪切板功能):在淘寶app中將鏈接自定義成淘口令,引導(dǎo)用戶進(jìn)行復(fù)制,并去QQ好友對(duì)話中粘貼。然后QQ好友收到消息進(jìn)行粘貼后后再打開自己的淘寶app,淘寶app每次從后臺(tái)切到前臺(tái)時(shí),就會(huì)檢查系統(tǒng)剪切板中是否有淘口令,如果有淘口令就進(jìn)行解析并跳轉(zhuǎn)到對(duì)于的商品頁面。
繪制方式:
1.使用 drawInContext drawRect 方法,方法會(huì)自動(dòng)創(chuàng)建畫布,只需要獲取上下文繪制。
2.手動(dòng)創(chuàng)建畫布
UIGraphicsBeginImageContext(CGSizeMake(400,400));
//獲取上下文
CGContextRef context =UIGraphicsGetCurrentContext();
//使用CG來繪制
CGFloat lineWidth = 2.f;
CGRect allRect =CGRectMake(0, 0, 100, 100);
CGRect circleRect =CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f);
CGPointcenter =CGPointMake(20, 20);
[[UIColor blueColor]setStroke];
[[UIColor grayColor]setFill];
CGContextSetLineWidth(context, lineWidth);
//繪制
CGContextStrokeEllipseInRect(context, circleRect);
//使用beizer繪制
CGFloat startAngle = - ((float)M_PI/ 2.f);
// Draw progress
UIBezierPath*processPath = [UIBezierPath bezierPath];
processPath.lineCapStyle=kCGLineCapButt;
processPath.lineWidth= lineWidth * 2.f;
CGFloatradius = 30;
CGFloatendAngle = 30 + startAngle;
[processPath addArcWithCenter:centerradius:radius startAngle:startAngle endAngle:endAngle clockwise:YES];
[[UIColor blackColor]set];
[processPath stroke];
//獲取繪制的圖片
UIImage* im =UIGraphicsGetImageFromCurrentImageContext();
//關(guān)閉上下文
UIGraphicsEndImageContext();
//將繪制的圖片顯示處理
UIImageView*imageView = [[UIImageViewalloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
[self.view addSubview:imageView];
[imageView setImage:im];
socket長連接設(shè)計(jì)(AsyncSocket):
使用單例模式維持一個(gè)長連接,并且定時(shí)地向服務(wù)器發(fā)送心跳包來判斷是否在連接中,否則重新連接??蛻舳诵枰獧z測連接狀態(tài),通過delegate來對(duì)連接狀態(tài)進(jìn)行回調(diào),包括:正在連接,連接中,連接斷開,重新連接。并且提供對(duì)應(yīng)的方法讓用戶去進(jìn)行一些常用的操作,包括:開始連接,退出登錄后的斷開連接,消息的收發(fā)等
當(dāng)const修飾一個(gè)指針變量的時(shí)候,可以指定指針不可修掛,或者指針指向的數(shù)據(jù)不可修改
const int *a(數(shù)據(jù)不可修改,指針可以) ?int *const a(指針可修改,數(shù)據(jù)不可變)
UIView,UIViewController,UIApplication直接繼承自UIResponder,而UIWindow繼承自UIView,UIResponder 下面有對(duì)應(yīng)的響應(yīng)用戶觸摸的方法
watchdog超時(shí)機(jī)制:
當(dāng)應(yīng)用未能及時(shí)響應(yīng)用戶界面事件,如在主線程操作網(wǎng)絡(luò),文件操作等耗時(shí)操作導(dǎo)致程序卡主了,就會(huì)會(huì)殺死程序并生成watchdog超時(shí)日志。
38.觀察者模式:
是一種訂閱-發(fā)布模式??梢酝瑫r(shí)對(duì)一個(gè)對(duì)象進(jìn)行觀察,當(dāng)對(duì)象的狀態(tài)發(fā)生改變,觀察者就會(huì)接收到對(duì)應(yīng)的通知,他的優(yōu)點(diǎn)在于觀察者與消息發(fā)布者并不需要知道對(duì)方的細(xì)節(jié),只需要做好自己的業(yè)務(wù),并且觀察者的數(shù)量并沒有限制,是一種低藕的一對(duì)多的觀察方式。
在ios里典型的就是NSNotification和KVO,前者常用語系統(tǒng)的事件通知,如鍵盤事件,前后臺(tái)切換,或者模塊之間的通信。KVO就是對(duì)對(duì)象屬性的觀察。
struct class 區(qū)別:
struct可以繼承,可以多態(tài),可以定義函數(shù),和class最大區(qū)別在于變量class的默認(rèn)訪問控制是private,而struct默認(rèn)是public;對(duì)于對(duì)象內(nèi)存的管理有系統(tǒng)的回收機(jī)制處理,而 struct 定義的對(duì)象則是在使用完畢后自動(dòng)清理分配的內(nèi)存。
當(dāng)需要有大量的邏輯處理使用類,如果只是CGPoint,CGSize這些輕量結(jié)構(gòu),使用struct。
枚舉typedef NS_ENUM
使用情景:當(dāng)需要列舉的狀態(tài)有限的,并且數(shù)量不多(3-4左右),使用枚舉
沙盒機(jī)制(用來存放非代碼文件(圖片,音頻,視頻,屬性列表(plist), sqlite數(shù)據(jù)庫,文本文件,其他等等)):
Document文件夾用于保存程序運(yùn)行中需要?jiǎng)?chuàng)建的文件,如sqlite文件
Library Preferences:存放應(yīng)用的偏好設(shè)置(有系統(tǒng)維護(hù),不能直接去修改,需要通過NSUserDefault來添加偏好設(shè)置)
Cache:緩存文件夾,在程序退出后不會(huì)刪除(iTunes不會(huì)備份)
tmp:臨時(shí)文件夾(在文件使用結(jié)束后刪除),在手機(jī)重啟目錄文件會(huì)被刪除;在內(nèi)存底的情況下也可能會(huì)被刪除,不會(huì)被iTunes備份
.app文件包,應(yīng)用程序本身,不能去修改。
如果你做個(gè)記事本的app,那么用戶寫了東西,總要把東西存起來。那么這個(gè)文件則是用戶自行生成的,就放在documents文件夾里面。
如果你有一個(gè)app,需要和服務(wù)器配合,經(jīng)常從服務(wù)器下載東西,展示給用戶看。那么這些下載下來的東西就放在library/cache
//獲取沙盒主目錄路徑
NSString *homeDir = NSHomeDirectory();
//獲取Documents目錄路徑
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
//獲取Library的目錄路徑
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) firstObject];
//獲取Caches目錄路徑
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
//獲取tmp目錄路徑
NSString *tmpDir =NSTemporaryDirectory();
進(jìn)程
ios中app的運(yùn)行是單進(jìn)程的,進(jìn)程是資源分配的基本單位,而線程是cpu調(diào)度的基本單位,是進(jìn)程中的一個(gè)實(shí)體。線程沒有獨(dú)立的存儲(chǔ)控件,進(jìn)程下的線程共享該進(jìn)程下的資源。
進(jìn)程間通信方式:
1.管道
2.消息隊(duì)列
3.socket
4.共享內(nèi)存(常用)
objc_msgForward消息轉(zhuǎn)發(fā)
當(dāng)我們在對(duì)一個(gè)對(duì)象發(fā)送消息的時(shí)候,運(yùn)行時(shí)調(diào)用objc_msgSend方法,根據(jù)累的分發(fā)表去尋找該方法,如果找不到就去父類找,如果依然找不到就會(huì)調(diào)用objc_msgForward進(jìn)行消息轉(zhuǎn)發(fā),首先它會(huì)嘗試去尋找在運(yùn)行的時(shí)候是否動(dòng)態(tài)地去實(shí)現(xiàn)了這個(gè)方法,如果沒有就去尋找有沒有其他的對(duì)象可以相應(yīng)該方法,如果仍然沒有,,則會(huì)調(diào)用forwardInvocation方法來來將消息轉(zhuǎn)發(fā)給其他的對(duì)象,如果以上三步都沒有處理掉就會(huì)跑出異常。
(_objc_msgForward是IMP類型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息,但它并沒有實(shí)現(xiàn)的時(shí)候)
BAD_ACCESS在什么情況下出現(xiàn)?
訪問了野指針,比如訪問已經(jīng)釋放的對(duì)象的成員變量或者向他發(fā)消息
所以當(dāng)指針變量所指向的對(duì)象內(nèi)存被釋放掉后,需要對(duì)指針賦值為nil,防止產(chǎn)生“野指針”。
檢測UIViewController內(nèi)存泄漏的原理:
1、添加一個(gè)myDealloc方法并且在里面輸出對(duì)應(yīng)的信息,在類方法load 里實(shí)現(xiàn)默認(rèn)的dealloc 與自定義定位方法進(jìn)行方法交換,那么根據(jù)打印處理的信息,就可以判斷一個(gè)VC有沒有釋放掉。
2、利用ARC中weak變量不持有對(duì)象,并且在對(duì)象釋放時(shí)會(huì)自動(dòng)置為nil的特性,在適當(dāng)?shù)臅r(shí)候來檢測VC是否在內(nèi)存駐留。
Left Join[左聯(lián)結(jié)]
返回包括左表中的所有記錄和右表中聯(lián)結(jié)字段相等的記錄
Right Join[右聯(lián)結(jié)]
返回包括右表中的所有記錄和右表中聯(lián)結(jié)字段相等的記錄
Inner Join[等值聯(lián)結(jié)]
只返回兩個(gè)表中字段相等的行
sqlite優(yōu)化:
1.查詢優(yōu)化:索引建立(防止全文檢索,防止索引建立太多)
2.大量的更新操作:事務(wù)(效率,安全)
3.表的建立(靈活,字段設(shè)置)
網(wǎng)絡(luò)請(qǐng)求中如何提高性能
1.在于服務(wù)器交換的數(shù)據(jù)格式方面,gson要比xml效率高,如果使用xml那么在大量數(shù)據(jù)的情況下使用基于事件的解析方式要比dom樹的方式效率高
2.對(duì)請(qǐng)求響應(yīng)的數(shù)據(jù)進(jìn)行壓縮 gzip
3.對(duì)請(qǐng)求數(shù)據(jù)進(jìn)行緩存,可以使用系統(tǒng)默認(rèn)的NSURLCache 對(duì)GET請(qǐng)求進(jìn)行緩存,只需要在請(qǐng)求時(shí)設(shè)置好對(duì)應(yīng)的緩存策略。
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3];
connection= [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
ios 循環(huán)引用
1.計(jì)時(shí)器NSTimer,當(dāng)我使用計(jì)時(shí)器時(shí)候,內(nèi)部會(huì)有一個(gè)引用指向VC,導(dǎo)致VC我發(fā)釋放,dealloc不會(huì)被調(diào)用。
2.delegate
3.block
如果賦值沒有通過setter方法或者KVC,而是直接修改屬性對(duì)應(yīng)的成員變量,例如:僅調(diào)用_name = @"newName",這時(shí)是不會(huì)觸發(fā)kvo機(jī)制
KVO
typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {
NSKeyValueObservingOptionNew = 0x01,//改變后的值
NSKeyValueObservingOptionOld = 0x02,//改變前的值
NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04, //addobserving之后會(huì)馬上調(diào)用observeValueForKeyPath,不會(huì)等到值改變
NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08//分2次調(diào)用。在值改變之前和值改變之后
};
FOUNDATION_EXPORT NSString *const NSKeyValueChangeNewKey;
FOUNDATION_EXPORT NSString *const NSKeyValueChangeOldKey;
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == (__bridge void*)self) {
if ([keyPath isEqualToString:kKeyPathForNavigationItemRightBarButtonItems]) {
//取值
NSArray *rightBarButtonItems = [change objectForKey:NSKeyValueChangeNewKey];
}
}
}
當(dāng)我們通過KVO對(duì)對(duì)象屬性進(jìn)行監(jiān)聽,如果注冊的選項(xiàng)使用NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld,那么在接受消息出會(huì)將新和舊的值都會(huì)傳遞過來。如果加上NSKeyValueObservingOptionPrior選項(xiàng)那么在改變值得前后都會(huì)個(gè)發(fā)一次通知。
原來類似于手動(dòng)觸發(fā)KVO通知(因?yàn)槟J(rèn)下類別添加的屬性因缺少setter getter方法而無法使用,通過runtime的關(guān)系屬性來添加屬性,如果需要對(duì)屬性的改變通過KVO進(jìn)行監(jiān)聽,因?yàn)橄到y(tǒng)這個(gè)時(shí)候是不會(huì)自動(dòng)觸發(fā)通知,所以就需要手動(dòng)去觸發(fā)):
[self willChangeValueForKey:@"mj_footer"]; // KVO
objc_setAssociatedObject(self, &MJRefreshFooterKey,//修改屬性
mj_footer, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:@"mj_footer"];//KVO
72.MJRefresh基本原理
對(duì)于使用下拉刷新使用的header刷新控件和下拉加載的footer刷新控件都是將這些控件放在scrollview的可見區(qū)域外,而對(duì)于下拉加載的footer控件的添加則要判斷scrollview的contentSize高度和frame的高度,如果內(nèi)容是超出可見區(qū)域content高度大于frame高度,則將footer空間添加到contentSize高度之后,否則就加在frame的高度之后。
因?yàn)閷?duì)于header還是footer刷新的動(dòng)作都是通過KVO對(duì)scrollview的y軸偏移量變化來處理,而整個(gè)下拉上拉的狀態(tài)可以在:默認(rèn)狀態(tài),正在下拉,刷新中,返回默認(rèn)狀態(tài)這幾個(gè)狀態(tài)中,所以將他們的這些特性抽象出來他們的父類,在里面添加KVO的消息機(jī)制,添加KVO所觸發(fā)的鉤子方法,不做任何實(shí)現(xiàn),具體實(shí)現(xiàn)在header,footer的類中根據(jù)偏移量的不同是處理不同的狀態(tài)變化,重寫狀態(tài)屬性的setter方法,當(dāng)偏移量發(fā)生改變,在對(duì)應(yīng)的響應(yīng)方法這里只需要判斷并且設(shè)置當(dāng)前刷新狀態(tài),而在狀態(tài)屬性的stter方法里,根據(jù)狀態(tài)的不同來設(shè)置不同的contentInset,視圖的改變。
在這里為了table或者collection的方便添加Header或者footer,添加scrollview的類別,這樣就需要在里面使用到下拉刷新的header屬性和上啦加載的footer屬性,這個(gè)時(shí)候就是用runtime機(jī)制的關(guān)系屬性的方法來處理,并且在這兩個(gè)setter方法添加KVO的手動(dòng)觸發(fā)通知willChangeValueForKey和didChangeValueForKey。
73.通過UIPanGestureRecognizer實(shí)現(xiàn)控件的拖拽效果
-(void)panAction:(UIPanGestureRecognizer *)pan
{
//以self.playerView的左上角為坐標(biāo)原點(diǎn)
//CGPoint point=[pan locationInView:self.pointView];
//獲取的點(diǎn)是以手指按下的點(diǎn)為原點(diǎn)的
CGPoint point1 = [pan translationInView:pan.view];
UIView *targetView = pan.view;
targetView.center = CGPointMake(targetView.center.x + point1.x, targetView.center.y + point1.y);
//清空位移數(shù)據(jù),避免拖拽事件的位移疊加
[pan setTranslation:CGPointZero inView:pan.view];
}
數(shù)據(jù)傳值方式
1.變量之間的數(shù)據(jù)傳遞
2.指針類型之間的地址傳遞
3.代理設(shè)置模式的數(shù)據(jù)傳值
4.通過的block傳遞
5.系統(tǒng)通知傳值(userInfo)
crash日志分析
在我們每次編譯(需要將debug的選項(xiàng)改為dsym)或打包版本時(shí)候都會(huì)自動(dòng)產(chǎn)生一個(gè)dsym文件,這是一個(gè)16進(jìn)制的函數(shù)地址映射文件,首先檢查.dsym和.crash文件的UUID是一致的,然后通過這個(gè)文件和崩潰日志.crash文件,使用XCODE自帶的symbolicatecrash命令行工具,就可以導(dǎo)出一個(gè).log文件,里面就可以讓我們定位到程序哪里出問題。所以每次發(fā)布版本的時(shí)候就需要將.xcarchive文件夾保存下來,里面就有我們所需要的文件。這里還需要注意要。
Time Profiler
按照固定的時(shí)間間隔來跟蹤每一個(gè)線程的堆棧信息,從而讓我們方便地看到程序運(yùn)行過程中各個(gè)方法正在消耗CPU時(shí)間
id聲明的對(duì)象具有運(yùn)行時(shí)的特性,知道運(yùn)行時(shí)才回去決定他的類型,即可以指向任意繼承自NSObject的對(duì)象
82.判斷cell是否在屏幕內(nèi)
1. 獲取當(dāng)前cell對(duì)于tableview的位置 rectForRowAtIndexPath
2.獲取table y軸上的偏移量,將cell的frame的y坐標(biāo) - 偏移量,獲取相對(duì)于內(nèi)容視圖的位置
3.可視區(qū)域?yàn)閠able 的frame,然后通過函數(shù) CGRectIntersectsRect 判斷兩個(gè)位置是否存在重疊
4.這里如果是加入到導(dǎo)航欄中的table,還需要考慮到contentInset內(nèi)編劇
CGFloat offsetY = self.tableView.contentOffset.y;
CGRect contentRect = self.tableView.frame;
CGRect rectCell = [self.tableView rectForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
rectCell.origin.y = rectCell.origin.y - offsetY;
if(CGRectIntersectsRect(contentRect, rectCell)){
NSLog(@"再屏幕內(nèi)");
}else{
NSLog(@"--不再屏幕內(nèi)");
}
解決UITableView中Cell重用機(jī)制導(dǎo)致內(nèi)容出錯(cuò)的方法總結(jié)
1.不使用重用機(jī)制
// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改為以下的方法
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根據(jù)indexPath準(zhǔn)確地取出一行
每個(gè)cell指定不同的重用標(biāo)識(shí)符
2.內(nèi)容出錯(cuò)往往是加載過慢導(dǎo)致內(nèi)容沒有及時(shí)地更新,可以使用縮略圖,顯示默認(rèn)圖片,預(yù)加載進(jìn)緩存中等方式處理。
哪些途徑可以讓ViewController瘦下來
1.將視圖處理單獨(dú)封裝(如各種窗口,cell)
2.將業(yè)務(wù)邏輯的處理,如網(wǎng)絡(luò)數(shù)據(jù)庫處理等可以放到對(duì)應(yīng)的model里進(jìn)行
Objective-C如何對(duì)【已有的方法】,添加自己的功能代碼以實(shí)現(xiàn)類似記錄日志這樣的功能?
1.創(chuàng)建一個(gè)方法,在里面調(diào)用原有的方法,并且加上記錄功能
2.使用runtime 的方法交換,對(duì)原有方法和自定義的方法進(jìn)行交換。
CADisplayLink保持著和屏幕刷新率形同的頻率進(jìn)行回調(diào)
//創(chuàng)建對(duì)象
self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];
//注冊到runloop中
[_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];//停止
[self.displayLink invalidate];
self.displayLink = nil;
對(duì)于一些需要高頻率刷新以致到達(dá)流暢效果的,如動(dòng)畫,繪制,視頻畫面等,一般使用TA。
當(dāng)我們使用readonly去修飾屬性,意味著不會(huì)生成setter方法,那么通過訪問方式如obj.name self.name這些實(shí)際就是訪問setter方法,但是因?yàn)闆]有所以這樣調(diào)用會(huì)報(bào)錯(cuò),所以修改屬性的方法就是在類內(nèi)部使用_name這種方式去修改。當(dāng)然可以用KVC的方式去修改(setter方法->accessInstanceVariablesDirectly yes->成員變量_name _isName name isName->setValues forUndefineKey該方法默認(rèn)拋異常,可以去重寫)
查找:
get<key>,<key>,is<key>?的順序方法查找getter方法,如果沒找到回去找類似于<key>AtIndex類似的數(shù)組方法。
+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認(rèn)行為),那么和先前的設(shè)值一樣,會(huì)按_key,_isKey,key,isKey?的順序搜索成員變量名,這里不推薦這么做,因?yàn)檫@樣直接訪問實(shí)例變量破壞了封裝性,使代碼更脆弱。如果重寫了類方法+(BOOL)accessInstanceVariablesDirectly返回NO的話,那么會(huì)直接調(diào)用valueForUndefinedKey:
還沒有找到的話,調(diào)用valueForUndefinedKey:
什么時(shí)候用@autoreleasepool
根據(jù)Apple的文檔,使用場景如下:
1.寫循環(huán),循環(huán)里面包含了大量臨時(shí)創(chuàng)建的對(duì)象。(本文的例子,相冊,本地文件的遍歷就會(huì)產(chǎn)生比較多開銷較大的對(duì)象,使用block來遍歷,在遍歷的時(shí)候會(huì)在內(nèi)部創(chuàng)建自動(dòng)釋放池來對(duì)對(duì)象及時(shí)釋放)
2.長時(shí)間在后臺(tái)運(yùn)行的任務(wù)。(如長時(shí)間運(yùn)行在后臺(tái)的網(wǎng)絡(luò)服務(wù))
97.github + xcode結(jié)合使用
特點(diǎn):
分布式:可以將代碼提交到本地代碼庫,在確定后再更新到服務(wù)器上。
流程:
一般在開發(fā)中,多人合作開發(fā)的時(shí)候,版本控制非常重要,所以一定要有穩(wěn)定的主分支Master,開發(fā)功能的分支developer,預(yù)發(fā)布的分支release這三個(gè)重要分支。每次有新功能和需求的時(shí)候每個(gè)開發(fā)人員就從developer分支分別拉取項(xiàng)目開發(fā),最后合并入developer,功能完成后就并入release,修改bug時(shí)在release分支操作,修復(fù)完成后分別并入Master和developer分支,最后從Master分支拉取最終的代碼打包上傳APP Store
常用命令:
commit 將代碼提交到本地代碼庫
push 將本地代碼庫提交到服務(wù)器
pull 將服務(wù)器代碼更新到本地上
merge 分之合并
viewController的生命周期
初始化:
1.initWithNibName(通過代碼調(diào)用,如present,pushNavigation)
2.initWithCoder(如果使用 storyboard 調(diào)用VC,VC這個(gè)時(shí)候是放在storyboard中),然后調(diào)用 awakeFromNib
如果view為 nil ?
loadView (系統(tǒng)通過代碼方式創(chuàng)建一個(gè)空的view),如果自己覆蓋,則需要同樣按照系統(tǒng)的方法去寫
UIView *view = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
self.view = view;
不要使用 [super loadView]
viewDidLoad:view加載完畢
viewWillAppear:控制器的view將要顯示
viewWillLayoutSubviews:控制器的view將要布局子控件
viewDidLayoutSubviews:控制器的view布局子控件完成
這期間系統(tǒng)可能會(huì)多次調(diào)用viewWillLayoutSubviews 、 ? ?viewDidLayoutSubviews 倆個(gè)方法
viewDidAppear:控制器的view完全顯示
viewWillDisappear:控制器的view即將消失的時(shí)候
viewDidDisappear:控制器的view完全消失的時(shí)候
loadView viewDidLoad
loadView方法在控制器的view為Nil的時(shí)候會(huì)調(diào)用,若控制器有關(guān)聯(lián)的 Xib 文件,該方法會(huì)從 Xib 文件中加載 view;如果沒有,則創(chuàng)建空白 UIView 對(duì)象。
如果用storyboard初始化控制器,就不用調(diào)用loadview方法了。如果重寫這個(gè)方法給控制器創(chuàng)建view則這個(gè)view必須是一個(gè)單例,而且不能被其他的控制器使用.并且不可以調(diào)用super。
不建議使用loadview,可以根據(jù)自己的需要在storyboard或者viewdidload中創(chuàng)建自己需要的view給控制器,如果使用 Interface Builder 創(chuàng)建 view,則務(wù)必不要重寫該方法。
viewDidlLoad :view 被加載到內(nèi)存后調(diào)用,不管什么情況都會(huì)被調(diào)用,用于視圖的初始化。
集合遍歷方法
1.對(duì)于數(shù)據(jù)量比較大,或者在便利過程中會(huì)產(chǎn)生一些消耗較大的臨時(shí),使用block的形式遍歷更好,因?yàn)槭褂枚嗑€程的方式,并且內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)autoreleasepool,對(duì)臨時(shí)創(chuàng)建的對(duì)象及時(shí)釋放。
2.一般的遍歷,如果不需要使用到下標(biāo),那么使用for in的方式更直觀效率也高。
3.對(duì)于block的數(shù)組方式,除了NSEnumerationConcurrent這種會(huì)在子線程內(nèi)遍歷,其余都是在主線程遍歷。NSEnumerationConcurrent使用了GCD的group原理,當(dāng)遍歷完成,返回主線程
常量
define 宏:只是在預(yù)處理器里進(jìn)行文本替換,不能聲明類型,會(huì)多次地分配內(nèi)存。除了定義常量,還可以定義函數(shù)宏
#define MIN(A,B) A < B ? A : B
const 常量:只分配一次內(nèi)存
修飾局部變量
static const (內(nèi)存分配在靜態(tài)區(qū),在運(yùn)行周期中保持一份)
聲明全局常量:
extern NSString *const kName?
UIKIT_EXTERN
FOUNDATION_EXPORT
autorelease 對(duì)象釋放
1.autorelease 本質(zhì)上就是延遲調(diào)用 release,autorelease pool,其實(shí)這個(gè)pool本質(zhì)上是一個(gè)stack,扔到pool中的對(duì)象等價(jià)于入棧
2.默認(rèn)下,對(duì)象會(huì)在當(dāng)前runloop迭代結(jié)束的時(shí)候釋放。
viewDidAppear 調(diào)用之前,NSAutoreleasePool會(huì)在當(dāng)前runLoop迭代結(jié)束的時(shí)候被銷毀,向池中的對(duì)象發(fā)送release消息,并且pop彈出棧中。
3.手動(dòng)指定 @autoreleasepool {},當(dāng)出了@autoreleasepool {}的作用域時(shí),當(dāng)前autoreleasepool被drain,其中的autoreleased對(duì)象被release
4.對(duì)于每一個(gè)Runloop,系統(tǒng)會(huì)隱式創(chuàng)建一個(gè)Autorelease pool(自然會(huì)有多個(gè)Autorelease pool),這樣所有的release pool會(huì)構(gòu)成一個(gè)象CallStack一樣的一個(gè)棧式結(jié)構(gòu),在每一個(gè)Runloop結(jié)束時(shí),當(dāng)前棧頂?shù)腁utorelease pool會(huì)被銷毀,這樣這個(gè)pool里的每個(gè)Object會(huì)被release。
- (void)viewDidLoad {
[superviewDidLoad];
//????@autoreleasepool?{
//????????NSString?*string?=?[NSString?stringWithFormat:@"leichunfeng"];//對(duì)象創(chuàng)建時(shí)計(jì)數(shù)器+1,有變量指向它,+1,計(jì)數(shù)器為= 2
//????????string_weak_?=?string;
//????}
//出了池的作用域,池被釋放,對(duì)象計(jì)數(shù)器 - 1,局部變量為nil,計(jì)數(shù)器-1,這時(shí)候?qū)ο蟊会尫?/p>
//????NSString?*string?=?nil;
//????@autoreleasepool?{
//????????string?=?[NSString?stringWithFormat:@"leichunfeng"];
//????????string_weak_?=?string;
//????}
出了池的作用域,池被釋放,對(duì)象計(jì)數(shù)器 - 1,局部變量這個(gè)時(shí)候還存,所以只有在viewdidload結(jié)束的時(shí)候,才會(huì)置nil,計(jì)數(shù)器-1,這時(shí)候?qū)ο蟊会尫牛詫?duì)象在viewdidload內(nèi)還存在,在viewwillappear才會(huì)是nil
NSLog(@"string:?%@",?string_weak_);
}
block對(duì)象就是一個(gè)結(jié)構(gòu)體,里面有isa指針指向自己的類(global malloc stack),有desc結(jié)構(gòu)體描述block的信息,引用到的__block變量,最重要的block結(jié)構(gòu)體有一個(gè)函數(shù)指針,指向block代碼塊。
KVO,NSNotification Delegate都是同步發(fā)送消息的