首先解釋ARC: automatic reference counting自動引用計數。
ARC幾個要點:
在對象被創建時 retain count +1,在對象被release時 retain count -1.當retain count 為0 時,銷毀對象。
程序中加入autoreleasepool的對象會由系統自動加上autorelease方法,如果該對象引用計數為0,則銷毀。
那麼ARC是為了解決什麼問題誕生的呢?這個得追溯到MRC手動內存管理時代說起。
MRC下內存管理的缺點:
1.當我們要釋放一個堆內存時,首先要確定指向這個堆空間的指針都被release了。(避免提前釋放)
2.釋放指針指向的堆空間,首先要確定哪些指針指向同一個堆,這些指針只能釋放一次。(MRC下即誰創建,誰釋放,避免重復釋放)
3.模塊化操作時,對象可能被多個模塊創建和使用,不能確定最後由誰去釋放。
4.多線程操作時,不確定哪個線程最後使用完畢
assign適用於基本數據類型,weak是適用於NSObject對象,並且是一個弱引用。
assign其實也可以用來修飾對象,那麼我們為什麼不用它呢?因為被assign修飾的對象在釋放之後,指針的地址還是存在的,也就是說指針並沒有被置為nil。如果在後續的內存分配中,剛好分到了這塊地址,程序就會崩潰掉。
而weak修飾的對象在釋放之後,指針地址會被置為nil。所以現在一般弱引用就是用weak。
首先__block是用來修飾一個變量,這個變量就可以在block中被修改(參考block實現原理)
__block:使用__block修飾的變量在block代碼快中會被retain(ARC下,MRC下不會retain)
__weak:使用__weak修飾的變量不會在block代碼塊中被retain
同時,在ARC下,要避免block出現循環引用 __weak typedof(self)weakSelf = self;
是不一樣的。
在MRC中__block variable在block中使用是不會retain的
但是ARC中__block則是會Retain的。
取而代之的是用__weak或是__unsafe_unretained來更精確的描述weak reference的目的
其中前者只能在iOS5之後可以使用,但是比較好 (該物件release之後,此pointer會自動設成nil)
而後者是ARC的環境下為了相容4.x的解決方案。
所以上面的範例中
__block MyClass* temp = …; // MRC環境下使用
__weak MyClass* temp = …; // ARC但只支援iOS5.0以上的版本
__unsafe_retained MyClass* temp = …; //ARC且可以相容4.x以後的版本
不是的。
atomic原子操作,系統會為setter方法加鎖。 具體使用 @synchronized(self){//code }
nonatomic不會為setter方法加鎖。
atomic:線程安全,需要消耗大量系統資源來為屬性加鎖
nonatomic:非線程安全,適合內存較小的移動設備
block中的循環引用:一個viewController
@property (nonatomic,strong)HttpRequestHandler * handler;
@property (nonatomic,strong)NSData *data;
_handler = [httpRequestHandler sharedManager];
[ downloadData:^(id responseData){
_data = responseData;
}];
self 擁有_handler, _handler 擁有block, block擁有self(因為使用了self的_data屬性,block會copy 一份self)
解決方法:
__weak typedof(self)weakSelf = self
[ downloadData:^(id responseData){
weakSelf.data = responseData;
}];
在Objective-C中,runtime會自動調用每個類的兩個方法。+load會在類初始加載時調用,+initialize會在第一次調用類的類方法或實例方法之前被調用。這兩個方法是可選的,且只有在實現了它們時才會被調用。
共同點:兩個方法都只會被調用一次。
先來看看怎麼理解發送消息的含義:
曾經覺得Objc特別方便上手,面對著 Cocoa 中大量 API,只知道簡單的查文檔和調用。還記得初學 Objective-C 時把[receiver message]當成簡單的方法調用,而無視了“發送消息”這句話的深刻含義。於是[receiver message]會被編譯器轉化為:
objc_msgSend(receiver, selector)
如果消息含有參數,則為:
objc_msgSend(receiver, selector, arg1, arg2, ...)
如果消息的接收者能夠找到對應的selector,那麼就相當於直接執行了接收者這個對象的特定方法;否則,消息要麼被轉發,或是臨時向接收者動態添加這個selector對應的實現內容,要麼就干脆玩完崩潰掉。
現在可以看出[receiver message]真的不是一個簡簡單單的方法調用。因為這只是在編譯階段確定了要向接收者發送message這條消息,而receive將要如何響應這條消息,那就要看運行時發生的情況來決定了。
Objective-C 的 Runtime 鑄就了它動態語言的特性,這些深層次的知識雖然平時寫代碼用的少一些,但是卻是每個 Objc 程序員需要了解的。
Objc Runtime使得C具有了面向對象能力,在程序運行時創建,檢查,修改類、對象和它們的方法。可以使用runtime的一系列方法實現。
順便附上OC中一個類的數據結構 /usr/include/objc/runtime.h
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class,因為Objc的類的本身也是一個Object,為了處理這個關系,r untime就創造了Meta Class,當給類發送[NSObject alloc]這樣消息時,實際上是把這個消息發給了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認為0
long info OBJC2_UNAVAILABLE; // 類信息,供運行期使用的一些位標識
long instance_size OBJC2_UNAVAILABLE; // 該類的實例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對象接到一個消息會根據isa指針查找消息對象,這時會在method Lists中遍歷,如果cache了,常用的方法調用時就能夠提高調用的效率。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協議鏈表
#endif
} OBJC2_UNAVAILABLE;
OC中一個類的對象實例的數據結構(/usr/include/objc/objc.h):
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
向object發送消息時,Runtime庫會根據object的isa指針找到這個實例object所屬於的類,然後在類的方法列表以及父類方法列表尋找對應的方法運行。id是一個objc_object結構類型的指針,這個類型的對象能夠轉換成任何一種對象。
然後再來看看消息發送的函數:objc_msgSend函數
在引言中已經對objc_msgSend進行了一點介紹,看起來像是objc_msgSend返回了數據,其實objc_msgSend從不返回數據而是你的方法被調用後返回了數據。下面詳細敘述下消息發送步驟:
檢測這個 selector 是不是要忽略的。比如 Mac OS X 開發,有了垃圾回收就不理會 retain,release 這些函數了。
檢測這個 target 是不是 nil 對象。ObjC 的特性是允許對一個 nil 對象執行任何一個方法不會 Crash,因為會被忽略掉。
如果上面兩個都過了,那就開始查找這個類的 IMP,先從 cache 裡面找,完了找得到就跳到對應的函數去執行。
如果 cache 找不到就找一下方法分發表。
如果分發表找不到就到超類的分發表去找,一直找,直到找到NSObject類為止。
如果還找不到就要開始進入動態方法解析了,後面會提到。
後面還有:
動態方法解析resolveThisMethodDynamically
消息轉發forwardingTargetForSelector
Method Swizzling 原理(方法攪拌?)
在Objective-C中調用一個方法,其實是向一個對象發送消息,查找消息的唯一依據是selector的名字。利用Objective-C的動態特性,可以實現在運行時偷換selector對應的方法實現,達到給方法掛鉤的目的。
每個類都有一個方法列表,存放著selector的名字和方法實現的映射關系。IMP有點類似函數指針,指向具體的Method實現。
我們可以利用 method_exchangeImplementations 來交換2個方法中的IMP,
我們可以利用 class_replaceMethod 來修改類,
我們可以利用 method_setImplementation 來直接設置某個方法的IMP,
……
歸根結底,都是偷換了selector的IMP,如下圖所示:
1.UIView是iOS系統中界面元素的基礎,所有的界面元素都繼承自它。它本身完全是由CoreAnimation來實現的 (Mac下似乎不是這樣)。它真正的繪圖部分,是由一個叫CALayer(Core Animation Layer)的類來管理。 UIView本身,更像是一個CALayer的管理器,訪問它的跟繪圖和跟坐標有關的屬性,例如frame,bounds等 等,實際上內部都是在訪問它所包含的CALayer的相關屬性。
2.UIView有個layer屬性,可以返回它的主CALayer實例,UIView有一個layerClass方法,返回主layer所使用的 類,UIView的子類,可以通過重載這個方法,來讓UIView使用不同的CALayer來顯示,例如通過
- (class) layerClass {
return ([CAEAGLLayer class]);
}
=使某個UIView的子類使用GL來進行繪制。
3.UIView的CALayer類似UIView的子View樹形結構,也可以向它的layer上添加子layer,來完成某些特殊的表 示。例如下面的代碼
grayCover = [[CALayer alloc] init];
grayCover.backgroundColor = [[[UIColor blackColor] colorWithAlphaComponent:0.2] CGColor];
[self.layer addSubLayer: grayCover];
會在目標View上敷上一層黑色的透明薄膜。
4.UIView的layer樹形在系統內部,被系統維護著三份copy(這段理解有點吃不准)。
邏輯樹,就是代碼裡可以操縱的,例如更改layer的屬性等等就在這一份。動畫樹,這是一個中間層,系統正在這一層上更改屬性,進行各種渲染操作。顯示樹,這棵樹的內容是當前正被顯示在屏幕上的內容。
這三棵樹的邏輯結構都是一樣的,區別只有各自的屬性。
我覺得應該是使用Quartz2D直接繪制圖片,得把這個看看。
步驟:
a、創建目標大小(cropWidth,cropHeight)的畫布。
b、使用UIImage的drawInRect方法進行繪制的時候,指定rect為(-x,-y,width,height)。
c、從畫布中得到裁剪後的圖像。
- (UIImage*)cropImageWithRect:(CGRect)cropRect
{
CGRect drawRect = CGRectMake(-cropRect.origin.x , -cropRect.origin.y, self.size.width * self.scale, self.size.height * self.scale);
UIGraphicsBeginImageContext(cropRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextClearRect(context, CGRectMake(0, 0, cropRect.size.width, cropRect.size.height));
[self drawInRect:drawRect];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end
drawRect方法依賴Core Graphics框架來進行自定義的繪制,但這種方法主要的缺點就是它處理touch事件的方式:每次按鈕被點擊後,都會用setNeddsDisplay進行強制重繪;而且不止一次,每次單點事件觸發兩次執行。這樣的話從性能的角度來說,對CPU和內存來說都是欠佳的。特別是如果在我們的界面上有多個這樣的UIButton實例。
圖片的內存緩存,可以考慮將圖片數據保存到一個數據模型中。所以在程序運行時這個模型都存在內存中。
移除策略:釋放數據模型對象。
當你訪問一個ViewController的view屬性時,如果此時view的值是nil,那麼,ViewController就會自動調用loadView這個方法。這個方法就會加載或者創建一個view對象,賦值給view屬性。
loadView默認做的事情是:如果此ViewController存在一個對應的nib文件,那麼就加載這個nib。否則,就創建一個UIView對象。
如果你用Interface Builder來創建界面,那麼不應該重載這個方法。
如果你想自己創建view對象,那麼可以重載這個方法。此時你需要自己給view屬性賦值。你自定義的方法不應該調用super。如果你需要對view做一些其他的定制操作,在viewDidLoad裡面去做。
=========================================
根據上面的文檔可以知道,有兩種情況:
1、如果你用了nib文件,重載這個方法就沒有太大意義。因為loadView的作用就是加載nib。如果你重載了這個方法不調用super,那麼nib文件就不會被加載。如果調用了super,那麼view已經加載完了,你需要做的其他事情在viewDidLoad裡面做更合適。
2、如果你沒有用nib,這個方法默認就是創建一個空的view對象。如果你想自己控制view對象的創建,例如創建一個特殊尺寸的view,那麼可以重載這個方法,自己創建一個UIView對象,然後指定 self.view = myView; 但這種情況也沒有必要調用super,因為反正你也不需要在super方法裡面創建的view對象。如果調用了super,那麼就是浪費了一些資源而已
橫豎屏切換的時候,系統會響應一些函數,其中 viewWillLayoutSubviews 和 viewDidLayoutSubviews。
//
- (void)viewWillLayoutSubviews
{
[self _shouldRotateToOrientation:(UIDeviceOrientation)[UIApplication sharedApplication].statusBarOrientation];
}
-(void)_shouldRotateToOrientation:(UIDeviceOrientation)orientation {
if (orientation == UIDeviceOrientationPortrait ||orientation ==
UIDeviceOrientationPortraitUpsideDown) {
// 豎屏
}
else {
// 橫屏
}
}
通過上述一個函數就知道橫豎屏切換的接口了。
注意:viewWillLayoutSubviews只能用在ViewController裡面,在view裡面沒有響應。
1.主隊列 dispatch_main_queue(); 串行 ,更新UI
2.全局隊列 dispatch_global_queue(); 並行,四個優先級:background,low,default,high
3.自定義隊列 dispatch_queue_t queue ; 可以自定義是並行:DISPATCH_QUEUE_CONCURRENT或者串行DISPATCH_QUEUE_SERIAL
1.GET請求的數據會附在URL之後(就是把數據放置在HTTP協議頭中),以?分割URL和傳輸數據,參數之間以&相連,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果數據是英文字母/數字,原樣發送,如果是空格,轉換為+,如果是中文/其他字符,則直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX為該符號以16進制表示的ASCII。
POST把提交的數據則放置在是HTTP包的包體中。
2.”GET方式提交的數據最多只能是1024字節,理論上POST沒有限制,可傳較大量的數據,IIS4中最大為80KB,IIS5中為100KB”??!
以上這句是我從其他文章轉過來的,其實這樣說是錯誤的,不准確的:
(1).首先是”GET方式提交的數據最多只能是1024字節”,因為GET是通過URL提交數據,那麼GET可提交的數據量就跟URL的長度有直接關系了。而實際上,URL不存在參數上限的問題,HTTP協議規范沒有對URL長度進行限制。這個限制是特定的浏覽器及服務器對它的限制。IE對URL長度的限制是2083字節(2K+35)。對於其他浏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於操作系統的支持。
注意這是限制是整個URL長度,而不僅僅是你的參數值數據長度。[見參考資料5]
(2).理論上講,POST是沒有大小限制的,HTTP協議規范也沒有進行大小限制,說“POST數據量存在80K/100K的大小限制”是不准確的,POST數據是沒有限制的,起限制作用的是服務器的處理程序的處理能力。
3.在ASP中,服務端獲取GET請求參數用Request.QueryString,獲取POST請求參數用Request.Form。在JSP中,用request.getParameter(\”XXXX\”)來獲取,雖然jsp中也有request.getQueryString()方法,但使用起來比較麻煩,比如:傳一個test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用GET和_POST分別獲取GET和POST中的數據,而REQUEST則可以獲取GET和POST兩種請求中的數據。值得注意的是,JSP中使用request和PHP中使用_REQUEST都會有隱患,這個下次再寫個文章總結。
4.POST的安全性要比GET的安全性高。注意:這裡所說的安全性和上面GET提到的“安全”不是同個概念。上面“安全”的含義僅僅是不作數據修改,而這裡安全的含義是真正的Security的含義,比如:通過GET提交數據,用戶名和密碼將明文出現在URL上,因為(1)登錄頁面有可能被浏覽器緩存,(2)其他人查看浏覽器的歷史紀錄,那麼別人就可以拿到你的賬號和密碼了,除此之外,使用GET提交數據還可能會造成Cross-site request forgery攻擊。
總結一下,Get是向服務器發索取數據的一種請求,而Post是向服務器提交數據的一種請求,在FORM(表單)中,Method默認為”GET”,實質上,GET和POST只是發送機制不同,並不是一個取一個發!
Binary search tree:二叉搜索樹。
主要由四個方法:(用C語言實現或者Python)
1.search:時間復雜度為O(h),h為樹的高度
2.traversal:時間復雜度為O(n),n為樹的總結點數。
3.insert:時間復雜度為O(h),h為樹的高度。
4.delete:最壞情況下,時間復雜度為O(h)+指針的移動開銷。
可以看到,二叉搜索樹的dictionary operation的時間復雜度與樹的高度h相關。所以需要盡可能的降低樹的高度,由此引出平衡二叉樹Balanced binary tree。它要求左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡二叉樹。這樣就可以將搜索樹的高度盡量減小。常用算法有紅黑樹、AVL、Treap、伸展樹等。