筆記2

?OC的動態(tài)性:會把編譯和鏈接是需要執(zhí)行的邏輯延遲到運行時,例如使用 id 所修飾的變量會在運行的時候才確定具體類型是什么,runtime 的方法交換等。


循環(huán)引用:當一個對象間接或直接地持有另一個對象与斤,而這個對象又有強指針指向該對象,就會引起循環(huán)應用無法釋放的問題。

1.block的使用 使用 weakSelf處理

2.delegate 作為屬性用weak修飾幽告,也可以防止野指針的出現(xiàn)

3.NSTimer 最為成員變量梅鹦。解決方式

在程序中除了在 dealloc方法去明確使用 invalid 然后賦值為nil

如果無法明確知道什么時候才可以停止裆甩,可以添加了一個類別讓原來的通過@selector執(zhí)行任務的方式轉換為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();

}

}

然后當控制釋放的時候,停止定時器就可以

-(void)dealloc{

NSLog(@"dealloc");

[self.timer invalidate];

self.timer = nil;

NSLog(@"timer:%@",self.timer);

}


屬性修飾

assign:基本數(shù)據(jù)類型嗤栓,結構體

weak:delegate(野指針冻河,循環(huán)引用),IB引進來的UI控件(視圖層級結構)

copy:不可變類型(指向不同對象)茉帅,block(保證是放在堆上)

strong:修飾對象叨叙,強引用

retain:和strong類似,在修飾block的時候作用相當于assign


類別作用:

在保持原有類的情況下堪澎,增加其方法(UIScrollView擂错,圖片緩存裁剪處理)

講一個龐大的類根據(jù)作用分解到不同的類別中,便于維護


static修飾的變量并不會改變作用范圍樱蛤,改變的只是內存分配方式(存放在靜態(tài)區(qū)在整個運行期間一直存在)


數(shù)據(jù)持久化:原理都是數(shù)據(jù)保存到本地文件中

1.屬性列表:一般使用NSUserDefaults操作(沙盒l(wèi)ibrary/preference)

2.對象歸檔:將對象轉化為NSData寫入帶本地文件钮呀,對象需要實現(xiàn)NSCoding協(xié)議

[NSKeyedArchiver archiveRootObject:self toFile:fileName];

3.SQLite3:FMDB封裝庫

4.Core Data:對SQLite基于面向對象的封裝


棧區(qū)(stack)由編譯器自動分配釋放,存放方法(函數(shù))的參數(shù)值,局部變量的值等,是一塊連續(xù)的內存的區(qū)域昨凡。即棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的爽醋。

堆區(qū)(heap)一般由程序員分配釋放,是不連續(xù)的內存區(qū)域,從而堆獲得的空間比較靈活便脊。有我們去new/alloc來創(chuàng)建的對象蚂四,就是放在堆下面。


棧區(qū)(stack)由編譯器自動分配釋放,存放方法(函數(shù))的參數(shù)值,局部變量的值等哪痰,是一塊連續(xù)的內存的區(qū)域遂赠。即棧頂?shù)牡刂泛蜅5淖畲笕萘渴窍到y(tǒng)預先規(guī)定好的。

堆區(qū)(heap)一般由程序員分配釋放,是不連續(xù)的內存區(qū)域晌杰,從而堆獲得的空間比較靈活解愤。有我們去new/alloc來創(chuàng)建的對象,就是放在堆下面乎莉。


結構體:

struct CGPoint {

CGFloat x;

CGFloat y;

};

typedef struct CGPoint CGPoint;


TableViewCell \ 頁面優(yōu)化:

1.盡量減少透明圖層的使用來減少渲染次數(shù)送讲,opaque = YES

2.減少對象的創(chuàng)建或者使用更為輕的對象,如當視圖元素不需要響應用戶的觸摸事件使用CALayer代替UIView

3.當大量文字需要顯示惋啃,cpu需要耗費比較多的資源去進行渲染和排版哼鬓,所以這個時候可以選擇CoreText 來處理

4.避免大圖使用,程序解碼比較耗時

5.復雜布局使用autoLayout會影響性能边灭,更好的是選擇手動布局

6.使用CoreGraphics進行繪制的時候异希,如果繪制比較耗時,可以異步處理后再返回主線程顯示

7.TableViewCell的重用绒瘦,避免在heightForCell方法進行較多的處理称簿,將動態(tài)高度的緩存

7.離屏渲染(系統(tǒng)會在屏幕緩沖外開辟新的緩沖去處理)扣癣,設置shadow時候不設置shadowPath,在設置圓角不同時使用 maskToBound和cornerRdius有時會導致憨降,或者使用CAShapeLayer 和BezierPath結合的的方式繪制圓角父虑。

7.如果不需要要顯示透明圖層,那么將試圖的opaque設置為YES(默認)授药,會讓系統(tǒng)會將試圖作為不透明去處理而加快渲染士嚎。

但如果需要使用到透明,則設置為NO悔叽。

8.減少視圖的層級結構

4.可以根據(jù)需要是使用預加載或者延遲加載莱衩。

8.使用Instrument工具去檢測


線程加鎖 : 解決多線程同時訪問同一資源導致的問題,一般通過加鎖處理或者使用串行隊列處理:

//串行隊列

dispatch_queue_create("", DISPATCH_QUEUE_SERIAL), ^ {

}

1.GCD信號量

+(NSString*)getContacts{

//獲取通訊錄權限

ABAuthorizationStatus authStatus = ABAddressBookGetAuthorizationStatus();

if(authStatus != kABAuthorizationStatusAuthorized)

{

//是否有通訊錄權限

__block BOOL accessGranted = NO;

ABAddressBookRef tmpAddressBook = ABAddressBookCreateWithOptions(NULL,NULL);

dispatch_semaphore_t sema=dispatch_semaphore_create(0);//創(chuàng)建信號量 為0

ABAddressBookRequestAccessWithCompletion(tmpAddressBook, ^(bool granted,CFErrorRef error){

accessGranted = granted;

dispatch_semaphore_signal(sema);//信號量 +1

});

//當信號量 > 0 才繼續(xù)運行娇澎,并且信號量 - 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.條件鎖(基于信號量)

NSConditionLock *conditionLock = nil;

BOOL canLock = [conditionLock tryLockWhenCondition:kCondition_A];

if (canLock) {

FDLog(@"線程A lock, 請等待");

[conditionLock unlock];

}else{

FDLog(@"線程A 條件不滿足笨蚁,未加lock");

}


4.NSRecursiveLock 遞歸鎖,同一個線程可以多次加鎖趟庄,但是不會引起死鎖,如果是NSLock括细,則會導致崩潰

- (void)reverseDebug:(NSUInteger )num lock:(NSRecursiveLock *)lock

{

? ? ?[lock lock];

? ? if (num<=0) {

? ? ? ? ?FDLog(@"結束");

? ? ? ? ?return;

? ? }

? ? [self reverseDebug:num-1 lock:lock];//遞歸

? ? [lock unlock];//還是需要解鎖

}


5.@synchronized(self) { ? ? ? ?

? ? ? ...

}

6.atomic的線程安全只限于setter和getter方法。會在setter方法里面加鎖岔激。


當我們去啟動 RunLoop勒极,系統(tǒng)會自動在內部創(chuàng)建自動釋放池。


數(shù)組遍歷

當使用block的方式進行遍歷虑鼎,會阻塞線程辱匿,直到遍歷結束。




UIView的 drawRect炫彩,layoutSubviews 方法調用時機:

兩者都是在視圖將要顯示(viewWillAppear之后匾七,viewDidAppear之前調用,layoutSubviews只要addSubviews就可以調用江兢,但是drawRect方法必須設置了frame才能調用昨忆,否則就不會被調用)

layoutSubviews

1、init初始化不會觸發(fā)layoutSubviews杉允。

2邑贴、addSubview會觸發(fā)layoutSubviews

3、設置view的Frame會觸發(fā)layoutSubviews叔磷,當然前提是frame的值設置前后發(fā)生了變化

4拢驾、滾動一個UIScrollView會觸發(fā)layoutSubviews

5、旋轉Screen會觸發(fā)父UIView上的layoutSubviews事件

6改基、改變一個UIView大小的時候也會觸發(fā)父UIView上的layoutSubviews事件

7繁疤、調用setNeedsLayout(延時到下個更新周期調用)


drawRect(在調用前會先調用sizeToFit方法,可以再這個方法計算坐標處理)

1.在UIView初始化時設置rect大小,drawRect 就會自動調用(在視圖將要顯示時候調用稠腊,只調用一次,viewDidLoad兩方法之后掉用的)躁染,否則不被自動調用。

2.調用setNeedsDisplay(異步執(zhí)行)


layoutSubviews:UIView 需要對子控件進行手動的布局架忌,則可以重寫此方法吞彤。只有在autoresizing和constraint-based behaviors of subviews不能提供我們想要的布局結果的時候,我們才應該重寫此方法鳖昌。

layoutSubviews方法調用先于drawRect



MVC優(yōu)點:

體現(xiàn)在低藕性與重用性:

數(shù)據(jù)層與視圖層的互相獨立备畦,可單獨地對某一模塊進行改變低飒,而不會影響到其余模塊许昨,也可以根據(jù)實際需要靈活地重用各個模塊。



NSOperation與GCD區(qū)別:

GCD是基于C語言的一套api褥赊;而NSOperaion底層是基于GCD開發(fā)糕档,因此效率方面GCD更好。

而NSOperation更適用于一些復雜的任務:

1.可以對隊列暫停拌喉,恢復速那,取消操作(NSOperationQueue cancelAllOperations setSuspended),不過這里暫停并不是暫停當前的任務而是下一個任務尿背,恢復會從第一個沒有執(zhí)行的任務開始端仰。

2.可以建立任務間的依賴關系(NSOperation setDependency)

3.NSBlockOperation NSInvocationOperation可以設置任務的優(yōu)先級(setQueuePriority),

而GCD只可以設置隊列的優(yōu)先級.



TCP三次握手:

客戶端向服務端發(fā)送請求 - 服務端返回數(shù)據(jù)包給客戶端 - 客戶端返回響應包給服務端田藐,連接建立

TCP釋放連接四次握手:

客戶端向服務端發(fā)送斷開請求 - 服務端接收到請求后先返回響應給客戶端荔烧,然后連接斷開后再返回一次數(shù)據(jù)包給客戶端- 客戶端收到服務端的返回,返回響應給服務端汽久。



9.CoreData多線程安全處理

CoreData包含(實體對象鹤竭,模型對象,上下文景醇,持久化存儲協(xié)調器)

線程注意點:

1.manageObject,manageObjectContext只能在自己的線程處理臀稚,不能跨線程

2.對于持久化存儲協(xié)調器(NSPersistentStoreCoordinator)可以多線程共享

3.magageObject的線程間傳遞通過id,并且通過objectWithID來獲取

解決方法:

共同使用一個 NSPersistentStoreCoordinator三痰,以及兩個獨立的 Contexts,一個context 負責主線程與UI協(xié)作吧寺,一個context在后臺負責耗時的處理,用Notifications的方式通知主線程的NSManagedObjectContext進行mergeChangesFromContextDidSaveNotification 對變化進行合并處理。

//這個通知是系統(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不能再主線程進行使用稚机,需要合并處理

[self.mainContext performBlock:^{

[self.mainContext mergeChangesFromContextDidSaveNotification:note];

}];

});

}

}];




14.網(wǎng)絡安全:

1.為了防止非法使用api,在每次向服務器請求的使用舷丹,將appKey和加密后的appSecret也發(fā)送給服務器抒钱,服務器根據(jù)appKey找到對應的appSecret與從客戶端傳遞過來進行比對,一致的話就是合法請求。(appkey 和 secret key相當于當前賬戶的另外一套賬號和密碼機制)

2.使用https協(xié)議谋币。



82.常見的crash情況

1.訪問野指針仗扬,比如訪問一個已經釋放的對象的屬性方法(解決方式是當一個指針所指向的對象如果已被釋放,則將指針置nil)

2.訪問數(shù)組類對象越界或插入了空對象

3.訪問了不存在的方法

4.當某個對象會被多個線程修改(加鎖處理)

5.NSNotification KVO的非對稱添加刪除(訪問野指針)


數(shù)據(jù)庫事務處理

當我們對數(shù)據(jù)庫進行更新操作時蕾额,默認會將操作一條條地往數(shù)據(jù)庫提交早芭。而使用事務處理,則是將所有的操作一次性提交給數(shù)據(jù)庫诅蝶,當處理的過程中出現(xiàn)問題退个,則可以進行回滾操作不進行任何修改,保證數(shù)據(jù)的完整性调炬。

所以當需要對大量的數(shù)據(jù)進行更新操作時语盈,使用事務在效率和安全性都會大大提高。



16.斷點續(xù)傳

//使用HEAD方法缰泡,僅獲取目標文件的信息刀荒,而不做實際的下載工作。

//[request setHTTPMethod:@"HEAD"];

/**

設置斷點續(xù)傳的思路:

HeaderField:頭域(請求頭部的字段)

可以通過指定range的范圍逐步地下載指定范圍內的數(shù)據(jù)棘钞,待下載完成后缠借,再將這些數(shù)據(jù)拼接成一個文件。

1根據(jù)HEAD方法獲取到要下載的文件的總大小宜猜、

2在磁盤上建立一個臨時的緩沖文件泼返,該文件的大小與目標文件大小一致


3緩沖文件中所有字節(jié)都是默認為0

4開啟多線程,分別加載不同的range頭指定的數(shù)據(jù)塊姨拥,待數(shù)據(jù)塊加載完成以后绅喉,將其分別寫入對應的偏移地址。

5所有數(shù)據(jù)下載完成以后垫毙,表示文件下載完成霹疫,將臨時文件名更改為目標文件。

開發(fā)的難點:

0在寫入文件之前综芥,首先要建立一個同等大小的文件丽蝎。

1文件的讀寫問題,在oc里默認是覆蓋膀藐,追加屠阻,如果要指定位置,需要用seek方法额各,移動文件指針国觉。

2在多線程寫入文件時,文件的鎖定操作是一個問題虾啦。

*/

[request setValue:@"bytes=0-499" forKeyPath:@"range"];//表di示只讀取數(shù)據(jù)的第0個字節(jié)到第499個字節(jié)麻诀。




線程間通信:

1.performSelectorOnMainThread痕寓,去主線程執(zhí)行

? ?performSelectorInBackground ,后臺線程執(zhí)行

2.通過NSMachPort端口

將端口添加到當前線程的runloop里,通過這個端口與其他線程進行消息的發(fā)送接收(通過delegate 來進行消息接受的回調蝇闭,通過回調方法傳遞過來的參數(shù)呻率,獲取對方的端口,通過端口就可以發(fā)送消息)呻引。

NSPort *myPort = [NSMachPort port];

//2. 設置port的代理回調對象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的方法跳轉到App2,并且在URL中帶上想要的參數(shù)(需要在info.plist中配置好URL types)逻悠,常用于 分享到微信朋友圈微博等功能元践。

2.Keychain:微信登陸,使用同一個賬號平臺童谒,實現(xiàn)自動登錄其他的平臺单旁。每個程序都有一個獨立的keychain存儲,只需要使用對應的登錄SDK惠啄,通過共享keychain中的數(shù)據(jù)慎恒,那么就可以實現(xiàn)統(tǒng)一賬戶登錄了任内。(比如密碼撵渡、證書等等,就需要使用更為安全的keychain了死嗦。keychain里保存的信息不會因App被刪除而丟失趋距,在用戶重新安裝App后依然有效,數(shù)據(jù)還在越除。它是一個sqlite數(shù)據(jù)庫节腐,其保存的所有數(shù)據(jù)都是加密過的。)

3.UIPasteboard(剪切板功能):在淘寶app中將鏈接自定義成淘口令摘盆,引導用戶進行復制翼雀,并去QQ好友對話中粘貼。然后QQ好友收到消息進行粘貼后后再打開自己的淘寶app孩擂,淘寶app每次從后臺切到前臺時狼渊,就會檢查系統(tǒng)剪切板中是否有淘口令,如果有淘口令就進行解析并跳轉到對于的商品頁面类垦。



繪制方式:

1.使用 drawInContext drawRect 方法狈邑,方法會自動創(chuàng)建畫布,只需要獲取上下文繪制蚤认。

2.手動創(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();

//關閉上下文

UIGraphicsEndImageContext();

//將繪制的圖片顯示處理

UIImageView*imageView = [[UIImageViewalloc]initWithFrame:CGRectMake(50, 50, 200, 200)];

[self.view addSubview:imageView];

[imageView setImage:im];


socket長連接設計(AsyncSocket):

使用單例模式維持一個長連接米苹,并且定時地向服務器發(fā)送心跳包來判斷是否在連接中,否則重新連接砰琢≌核唬客戶端需要檢測連接狀態(tài)良瞧,通過delegate來對連接狀態(tài)進行回調,包括:正在連接训唱,連接中莺褒,連接斷開,重新連接雪情。并且提供對應的方法讓用戶去進行一些常用的操作遵岩,包括:開始連接,退出登錄后的斷開連接巡通,消息的收發(fā)等


當const修飾一個指針變量的時候尘执,可以指定指針不可修掛,或者指針指向的數(shù)據(jù)不可修改

const int *a(數(shù)據(jù)不可修改宴凉,指針可以) ?int *const a(指針可修改誊锭,數(shù)據(jù)不可變)


UIView,UIViewController,UIApplication直接繼承自UIResponder,而UIWindow繼承自UIView弥锄,UIResponder 下面有對應的響應用戶觸摸的方法



watchdog超時機制:

當應用未能及時響應用戶界面事件丧靡,如在主線程操作網(wǎng)絡,文件操作等耗時操作導致程序卡主了籽暇,就會會殺死程序并生成watchdog超時日志温治。



38.觀察者模式:

是一種訂閱-發(fā)布模式〗溆疲可以同時對一個對象進行觀察熬荆,當對象的狀態(tài)發(fā)生改變,觀察者就會接收到對應的通知绸狐,他的優(yōu)點在于觀察者與消息發(fā)布者并不需要知道對方的細節(jié)卤恳,只需要做好自己的業(yè)務,并且觀察者的數(shù)量并沒有限制寒矿,是一種低藕的一對多的觀察方式突琳。

在ios里典型的就是NSNotification和KVO,前者常用語系統(tǒng)的事件通知,如鍵盤事件符相,前后臺切換拆融,或者模塊之間的通信。KVO就是對對象屬性的觀察主巍。



struct class 區(qū)別:

struct可以繼承冠息,可以多態(tài),可以定義函數(shù)孕索,和class最大區(qū)別在于變量class的默認訪問控制是private逛艰,而struct默認是public;對于對象內存的管理有系統(tǒng)的回收機制處理搞旭,而 struct 定義的對象則是在使用完畢后自動清理分配的內存散怖。

當需要有大量的邏輯處理使用類菇绵,如果只是CGPoint,CGSize這些輕量結構镇眷,使用struct咬最。



枚舉typedef NS_ENUM

使用情景:當需要列舉的狀態(tài)有限的,并且數(shù)量不多(3-4左右)欠动,使用枚舉



沙盒機制(用來存放非代碼文件(圖片,音頻,視頻,屬性列表(plist), sqlite數(shù)據(jù)庫,文本文件,其他等等)):

Document文件夾用于保存程序運行中需要創(chuàng)建的文件永乌,如sqlite文件

Library Preferences:存放應用的偏好設置(有系統(tǒng)維護,不能直接去修改具伍,需要通過NSUserDefault來添加偏好設置)

Cache:緩存文件夾翅雏,在程序退出后不會刪除(iTunes不會備份)

tmp:臨時文件夾(在文件使用結束后刪除),在手機重啟目錄文件會被刪除人芽;在內存底的情況下也可能會被刪除望几,不會被iTunes備份

.app文件包,應用程序本身萤厅,不能去修改橄抹。

如果你做個記事本的app,那么用戶寫了東西惕味,總要把東西存起來楼誓。那么這個文件則是用戶自行生成的,就放在documents文件夾里面赦拘。

如果你有一個app慌随,需要和服務器配合,經常從服務器下載東西躺同,展示給用戶看。那么這些下載下來的東西就放在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();


進程

ios中app的運行是單進程的丸逸,進程是資源分配的基本單位蹋艺,而線程是cpu調度的基本單位,是進程中的一個實體黄刚。線程沒有獨立的存儲控件捎谨,進程下的線程共享該進程下的資源。

進程間通信方式:

1.管道

2.消息隊列

3.socket

4.共享內存(常用)



objc_msgForward消息轉發(fā)

當我們在對一個對象發(fā)送消息的時候憔维,運行時調用objc_msgSend方法涛救,根據(jù)累的分發(fā)表去尋找該方法,如果找不到就去父類找业扒,如果依然找不到就會調用objc_msgForward進行消息轉發(fā)检吆,首先它會嘗試去尋找在運行的時候是否動態(tài)地去實現(xiàn)了這個方法,如果沒有就去尋找有沒有其他的對象可以相應該方法程储,如果仍然沒有蹭沛,臂寝,則會調用forwardInvocation方法來來將消息轉發(fā)給其他的對象,如果以上三步都沒有處理掉就會跑出異常摊灭。

(_objc_msgForward是IMP類型咆贬,用于消息轉發(fā)的:當向一個對象發(fā)送一條消息,但它并沒有實現(xiàn)的時候)


BAD_ACCESS在什么情況下出現(xiàn)帚呼?

訪問了野指針掏缎,比如訪問已經釋放的對象的成員變量或者向他發(fā)消息

所以當指針變量所指向的對象內存被釋放掉后,需要對指針賦值為nil煤杀,防止產生“野指針”御毅。



檢測UIViewController內存泄漏的原理:

1、添加一個myDealloc方法并且在里面輸出對應的信息怜珍,在類方法load 里實現(xiàn)默認的dealloc 與自定義定位方法進行方法交換端蛆,那么根據(jù)打印處理的信息,就可以判斷一個VC有沒有釋放掉酥泛。

2今豆、利用ARC中weak變量不持有對象,并且在對象釋放時會自動置為nil的特性柔袁,在適當?shù)臅r候來檢測VC是否在內存駐留呆躲。


Left Join[左聯(lián)結]

返回包括左表中的所有記錄和右表中聯(lián)結字段相等的記錄

Right Join[右聯(lián)結]

返回包括右表中的所有記錄和右表中聯(lián)結字段相等的記錄

Inner Join[等值聯(lián)結]

只返回兩個表中字段相等的行


sqlite優(yōu)化:

1.查詢優(yōu)化:索引建立(防止全文檢索,防止索引建立太多)

2.大量的更新操作:事務(效率捶索,安全)

3.表的建立(靈活弟孟,字段設置)




網(wǎng)絡請求中如何提高性能

1.在于服務器交換的數(shù)據(jù)格式方面,gson要比xml效率高别惦,如果使用xml那么在大量數(shù)據(jù)的情況下使用基于事件的解析方式要比dom樹的方式效率高

2.對請求響應的數(shù)據(jù)進行壓縮 gzip

3.對請求數(shù)據(jù)進行緩存,可以使用系統(tǒng)默認的NSURLCache 對GET請求進行緩存,只需要在請求時設置好對應的緩存策略趴腋。

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3];

connection= [[NSURLConnection alloc] initWithRequest:request delegate:self];

[connection start];


ios 循環(huán)引用

1.計時器NSTimer,當我使用計時器時候址芯,內部會有一個引用指向VC哥捕,導致VC我發(fā)釋放,dealloc不會被調用构回。

2.delegate

3.block



如果賦值沒有通過setter方法或者KVC夏块,而是直接修改屬性對應的成員變量,例如:僅調用_name = @"newName"纤掸,這時是不會觸發(fā)kvo機制



KVO

typedef NS_OPTIONS(NSUInteger, NSKeyValueObservingOptions) {

NSKeyValueObservingOptionNew = 0x01,//改變后的值

NSKeyValueObservingOptionOld = 0x02,//改變前的值

NSKeyValueObservingOptionInitial NS_ENUM_AVAILABLE(10_5, 2_0) = 0x04, //addobserving之后會馬上調用observeValueForKeyPath脐供,不會等到值改變

NSKeyValueObservingOptionPrior NS_ENUM_AVAILABLE(10_5, 2_0) = 0x08//分2次調用。在值改變之前和值改變之后

};

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];

}

}

}

當我們通過KVO對對象屬性進行監(jiān)聽借跪,如果注冊的選項使用NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld政己,那么在接受消息出會將新和舊的值都會傳遞過來。如果加上NSKeyValueObservingOptionPrior選項那么在改變值得前后都會個發(fā)一次通知垦梆。

原來類似于手動觸發(fā)KVO通知(因為默認下類別添加的屬性因缺少setter getter方法而無法使用匹颤,通過runtime的關系屬性來添加屬性仅孩,如果需要對屬性的改變通過KVO進行監(jiān)聽,因為系統(tǒng)這個時候是不會自動觸發(fā)通知印蓖,所以就需要手動去觸發(fā)):

[self willChangeValueForKey:@"mj_footer"]; // KVO

objc_setAssociatedObject(self, &MJRefreshFooterKey,//修改屬性

mj_footer, OBJC_ASSOCIATION_ASSIGN);

[self didChangeValueForKey:@"mj_footer"];//KVO





72.MJRefresh基本原理

對于使用下拉刷新使用的header刷新控件和下拉加載的footer刷新控件都是將這些控件放在scrollview的可見區(qū)域外辽慕,而對于下拉加載的footer控件的添加則要判斷scrollview的contentSize高度和frame的高度,如果內容是超出可見區(qū)域content高度大于frame高度赦肃,則將footer空間添加到contentSize高度之后溅蛉,否則就加在frame的高度之后。

因為對于header還是footer刷新的動作都是通過KVO對scrollview的y軸偏移量變化來處理他宛,而整個下拉上拉的狀態(tài)可以在:默認狀態(tài)船侧,正在下拉,刷新中厅各,返回默認狀態(tài)這幾個狀態(tài)中镜撩,所以將他們的這些特性抽象出來他們的父類,在里面添加KVO的消息機制队塘,添加KVO所觸發(fā)的鉤子方法袁梗,不做任何實現(xiàn),具體實現(xiàn)在header,footer的類中根據(jù)偏移量的不同是處理不同的狀態(tài)變化憔古,重寫狀態(tài)屬性的setter方法遮怜,當偏移量發(fā)生改變,在對應的響應方法這里只需要判斷并且設置當前刷新狀態(tài)鸿市,而在狀態(tài)屬性的stter方法里锯梁,根據(jù)狀態(tài)的不同來設置不同的contentInset,視圖的改變焰情。

在這里為了table或者collection的方便添加Header或者footer陌凳,添加scrollview的類別,這樣就需要在里面使用到下拉刷新的header屬性和上啦加載的footer屬性烙样,這個時候就是用runtime機制的關系屬性的方法來處理冯遂,并且在這兩個setter方法添加KVO的手動觸發(fā)通知willChangeValueForKey和didChangeValueForKey。





73.通過UIPanGestureRecognizer實現(xiàn)控件的拖拽效果

-(void)panAction:(UIPanGestureRecognizer *)pan

{

//以self.playerView的左上角為坐標原點

//CGPoint point=[pan locationInView:self.pointView];

//獲取的點是以手指按下的點為原點的

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ù)據(jù)傳值

4.通過的block傳遞

5.系統(tǒng)通知傳值(userInfo)



crash日志分析

在我們每次編譯(需要將debug的選項改為dsym)或打包版本時候都會自動產生一個dsym文件,這是一個16進制的函數(shù)地址映射文件壁却,首先檢查.dsym和.crash文件的UUID是一致的批狱,然后通過這個文件和崩潰日志.crash文件,使用XCODE自帶的symbolicatecrash命令行工具展东,就可以導出一個.log文件赔硫,里面就可以讓我們定位到程序哪里出問題。所以每次發(fā)布版本的時候就需要將.xcarchive文件夾保存下來盐肃,里面就有我們所需要的文件爪膊。這里還需要注意要权悟。


Time Profiler

按照固定的時間間隔來跟蹤每一個線程的堆棧信息,從而讓我們方便地看到程序運行過程中各個方法正在消耗CPU時間



id聲明的對象具有運行時的特性,知道運行時才回去決定他的類型推盛,即可以指向任意繼承自NSObject的對象



82.判斷cell是否在屏幕內

1. 獲取當前cell對于tableview的位置 rectForRowAtIndexPath

2.獲取table y軸上的偏移量峦阁,將cell的frame的y坐標 - 偏移量,獲取相對于內容視圖的位置

3.可視區(qū)域為table 的frame耘成,然后通過函數(shù) CGRectIntersectsRect 判斷兩個位置是否存在重疊

4.這里如果是加入到導航欄中的table榔昔,還需要考慮到contentInset內編劇


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(@"再屏幕內");

}else{

NSLog(@"--不再屏幕內");

}



解決UITableView中Cell重用機制導致內容出錯的方法總結

1.不使用重用機制

// UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改為以下的方法

UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根據(jù)indexPath準確地取出一行

每個cell指定不同的重用標識符

2.內容出錯往往是加載過慢導致內容沒有及時地更新,可以使用縮略圖瘪菌,顯示默認圖片撒会,預加載進緩存中等方式處理。


哪些途徑可以讓ViewController瘦下來

1.將視圖處理單獨封裝(如各種窗口师妙,cell)

2.將業(yè)務邏輯的處理诵肛,如網(wǎng)絡數(shù)據(jù)庫處理等可以放到對應的model里進行



Objective-C如何對【已有的方法】,添加自己的功能代碼以實現(xiàn)類似記錄日志這樣的功能默穴?

1.創(chuàng)建一個方法怔檩,在里面調用原有的方法,并且加上記錄功能

2.使用runtime 的方法交換壁顶,對原有方法和自定義的方法進行交換珠洗。


CADisplayLink保持著和屏幕刷新率形同的頻率進行回調

//創(chuàng)建對象

self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)];

//注冊到runloop中

[_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];//停止

[self.displayLink invalidate];

self.displayLink = nil;

對于一些需要高頻率刷新以致到達流暢效果的,如動畫若专,繪制许蓖,視頻畫面等,一般使用TA调衰。



當我們使用readonly去修飾屬性膊爪,意味著不會生成setter方法,那么通過訪問方式如obj.name self.name這些實際就是訪問setter方法嚎莉,但是因為沒有所以這樣調用會報錯米酬,所以修改屬性的方法就是在類內部使用_name這種方式去修改。當然可以用KVC的方式去修改(setter方法->accessInstanceVariablesDirectly yes->成員變量_name _isName name isName->setValues forUndefineKey該方法默認拋異常,可以去重寫)

查找:

get<key>趋箩,<key>赃额,is<key>?的順序方法查找getter方法,如果沒找到回去找類似于<key>AtIndex類似的數(shù)組方法叫确。

+ (BOOL)accessInstanceVariablesDirectly,如果返回YES(默認行為)跳芳,那么和先前的設值一樣,會按_key,_isKey,key,isKey?的順序搜索成員變量名竹勉,這里不推薦這么做飞盆,因為這樣直接訪問實例變量破壞了封裝性,使代碼更脆弱。如果重寫了類方法+(BOOL)accessInstanceVariablesDirectly返回NO的話吓歇,那么會直接調用valueForUndefinedKey:

還沒有找到的話孽水,調用valueForUndefinedKey:


什么時候用@autoreleasepool

根據(jù)Apple的文檔,使用場景如下:

1.寫循環(huán)城看,循環(huán)里面包含了大量臨時創(chuàng)建的對象女气。(本文的例子,相冊析命,本地文件的遍歷就會產生比較多開銷較大的對象主卫,使用block來遍歷,在遍歷的時候會在內部創(chuàng)建自動釋放池來對對象及時釋放)

2.長時間在后臺運行的任務鹃愤。(如長時間運行在后臺的網(wǎng)絡服務)



97.github + xcode結合使用

特點:

分布式:可以將代碼提交到本地代碼庫簇搅,在確定后再更新到服務器上。

流程:

一般在開發(fā)中软吐,多人合作開發(fā)的時候瘩将,版本控制非常重要,所以一定要有穩(wěn)定的主分支Master凹耙,開發(fā)功能的分支developer姿现,預發(fā)布的分支release這三個重要分支。每次有新功能和需求的時候每個開發(fā)人員就從developer分支分別拉取項目開發(fā)肖抱,最后合并入developer备典,功能完成后就并入release,修改bug時在release分支操作意述,修復完成后分別并入Master和developer分支提佣,最后從Master分支拉取最終的代碼打包上傳APP Store

常用命令:

commit 將代碼提交到本地代碼庫

push 將本地代碼庫提交到服務器

pull 將服務器代碼更新到本地上

merge 分之合并


viewController的生命周期

初始化:

1.initWithNibName(通過代碼調用,如present荤崇,pushNavigation)

2.initWithCoder(如果使用 storyboard 調用VC拌屏,VC這個時候是放在storyboard中),然后調用 awakeFromNib

如果view為 nil ?

loadView (系統(tǒng)通過代碼方式創(chuàng)建一個空的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)可能會多次調用viewWillLayoutSubviews 倚喂、 ? ?viewDidLayoutSubviews 倆個方法

viewDidAppear:控制器的view完全顯示

viewWillDisappear:控制器的view即將消失的時候

viewDidDisappear:控制器的view完全消失的時候


loadView viewDidLoad

loadView方法在控制器的view為Nil的時候會調用,若控制器有關聯(lián)的 Xib 文件瓣戚,該方法會從 Xib 文件中加載 view端圈;如果沒有,則創(chuàng)建空白 UIView 對象子库。

如果用storyboard初始化控制器,就不用調用loadview方法了枫笛。如果重寫這個方法給控制器創(chuàng)建view則這個view必須是一個單例,而且不能被其他的控制器使用.并且不可以調用super。

不建議使用loadview,可以根據(jù)自己的需要在storyboard或者viewdidload中創(chuàng)建自己需要的view給控制器刚照,如果使用 Interface Builder 創(chuàng)建 view,則務必不要重寫該方法喧兄。

viewDidlLoad :view 被加載到內存后調用无畔,不管什么情況都會被調用啊楚,用于視圖的初始化。


集合遍歷方法

1.對于數(shù)據(jù)量比較大浑彰,或者在便利過程中會產生一些消耗較大的臨時恭理,使用block的形式遍歷更好,因為使用多線程的方式郭变,并且內部會自動創(chuàng)建一個autoreleasepool颜价,對臨時創(chuàng)建的對象及時釋放。

2.一般的遍歷诉濒,如果不需要使用到下標周伦,那么使用for in的方式更直觀效率也高。

3.對于block的數(shù)組方式未荒,除了NSEnumerationConcurrent這種會在子線程內遍歷专挪,其余都是在主線程遍歷。NSEnumerationConcurrent使用了GCD的group原理片排,當遍歷完成寨腔,返回主線程


常量

define 宏:只是在預處理器里進行文本替換,不能聲明類型率寡,會多次地分配內存迫卢。除了定義常量,還可以定義函數(shù)宏

#define MIN(A,B) A < B ? A : B

const 常量:只分配一次內存

修飾局部變量

static const (內存分配在靜態(tài)區(qū)冶共,在運行周期中保持一份)

聲明全局常量:

extern NSString *const kName?

UIKIT_EXTERN

FOUNDATION_EXPORT


autorelease 對象釋放

1.autorelease 本質上就是延遲調用 release乾蛤,autorelease pool,其實這個pool本質上是一個stack比默,扔到pool中的對象等價于入棧

2.默認下幻捏,對象會在當前runloop迭代結束的時候釋放。

viewDidAppear 調用之前命咐,NSAutoreleasePool會在當前runLoop迭代結束的時候被銷毀篡九,向池中的對象發(fā)送release消息,并且pop彈出棧中醋奠。

3.手動指定 @autoreleasepool {}榛臼,當出了@autoreleasepool {}的作用域時,當前autoreleasepool被drain窜司,其中的autoreleased對象被release

4.對于每一個Runloop沛善,系統(tǒng)會隱式創(chuàng)建一個Autorelease pool(自然會有多個Autorelease pool),這樣所有的release pool會構成一個象CallStack一樣的一個棧式結構塞祈,在每一個Runloop結束時金刁,當前棧頂?shù)腁utorelease pool會被銷毀,這樣這個pool里的每個Object會被release。

- (void)viewDidLoad {

[superviewDidLoad];

//????@autoreleasepool?{

//????????NSString?*string?=?[NSString?stringWithFormat:@"leichunfeng"];//對象創(chuàng)建時計數(shù)器+1尤蛮,有變量指向它媳友,+1,計數(shù)器為= 2

//????????string_weak_?=?string;

//????}

//出了池的作用域产捞,池被釋放醇锚,對象計數(shù)器 - 1,局部變量為nil坯临,計數(shù)器-1焊唬,這時候對象被釋放


//????NSString?*string?=?nil;

//????@autoreleasepool?{

//????????string?=?[NSString?stringWithFormat:@"leichunfeng"];

//????????string_weak_?=?string;

//????}

出了池的作用域,池被釋放看靠,對象計數(shù)器 - 1赶促,局部變量這個時候還存,所以只有在viewdidload結束的時候衷笋,才會置nil芳杏,計數(shù)器-1,這時候對象被釋放辟宗,所以對象在viewdidload內還存在爵赵,在viewwillappear才會是nil

NSLog(@"string:?%@",?string_weak_);

}



block對象就是一個結構體,里面有isa指針指向自己的類(global malloc stack)泊脐,有desc結構體描述block的信息空幻,引用到的__block變量,最重要的block結構體有一個函數(shù)指針容客,指向block代碼塊秕铛。


KVO,NSNotification Delegate都是同步發(fā)送消息的


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末缩挑,一起剝皮案震驚了整個濱河市但两,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌供置,老刑警劉巖谨湘,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芥丧,居然都是意外死亡紧阔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門续担,熙熙樓的掌柜王于貴愁眉苦臉地迎上來擅耽,“玉大人,你說我怎么就攤上這事物遇」猿穑” “怎么了憾儒?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長这敬。 經常有香客問我航夺,道長,這世上最難降的妖魔是什么崔涂? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮始衅,結果婚禮上冷蚂,老公的妹妹穿的比我還像新娘。我一直安慰自己汛闸,他們只是感情好蝙茶,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诸老,像睡著了一般隆夯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上别伏,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天蹄衷,我揣著相機與錄音,去河邊找鬼厘肮。 笑死愧口,一個胖子當著我的面吹牛,可吹牛的內容都是我干的类茂。 我是一名探鬼主播耍属,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼巩检!你這毒婦竟也來了厚骗?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兢哭,失蹤者是張志新(化名)和其女友劉穎领舰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厦瓢,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡提揍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了煮仇。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劳跃。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖浙垫,靈堂內的尸體忽然破棺而出刨仑,到底是詐尸還是另有隱情郑诺,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布杉武,位于F島的核電站辙诞,受9級特大地震影響,放射性物質發(fā)生泄漏轻抱。R本人自食惡果不足惜飞涂,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祈搜。 院中可真熱鬧较店,春花似錦、人聲如沸容燕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蘸秘。三九已至官卡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間醋虏,已是汗流浹背寻咒。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灰粮,地道東北人仔涩。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像粘舟,于是被迫代替她去往敵國和親熔脂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,149評論 30 470
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評論 25 707
  • iOS網(wǎng)絡架構討論梳理整理中柑肴。霞揉。。 其實如果沒有APIManager這一層是沒法使用delegate的晰骑,畢竟多個單...
    yhtang閱讀 5,193評論 1 23
  • 望著這一張不成樣子的照片适秩,我心竊喜。 校園漸漸變得安靜了硕舆,十點一過秽荞,該是學生熄燈休息的時間點了。 ...
    一個南瓜先生閱讀 800評論 0 3
  • 馬甲線 ——懶惰是劣質生活的導火索钦听!SO關注健康洒试,認真生活從現(xiàn)在開始! 關注健康 ▼ 規(guī)律飲食 早餐──7:00(...
    馬甲線app閱讀 309評論 0 0