IOS 類文件.h和.m中@interface的區別
大家都知道我們在創建類文件時會發現:
#import <UIKit/UIKit.h>@interface ViewController : UIViewController@end
和
#import "ViewController.h"@interface ViewController ()@end
那么他們之間有何區別呢?
1、.h里的@interface,是典型的頭文件,它的屬性(@property)和方法(functions)都是能夠向其他類公開的。我們都知道有三種權限@private,@protected,@public。寫在.h里的默認是@protected權限。
2、.m里的@interface,我們也可以稱之為擴展(class extension),是.h文件中@interface的補充。可以增加屬性,方法和成員變量,但是只能在.m文件里可見,對外是不開放的。在.m里的默認是@private權限
@END.
@property的作用和atomic和nonatomic
參考
當我們寫下@property NSObject *foo時,編譯器幫我們做了以下幾件事:
創建實例變量_foo
聲明foo屬性的setter、getter方法
實現foo屬性的setter、getter方法
@property 的本質是什么?
@property = ivar + getter + setter;實例變量+get方法+set方法,也就是說使用@property 系統會自動生成setter和getter方法;
@property的屬性關鍵字詳解
atomic和nonatomic
atomic和nonatomic用來決定編譯器生成的getter和setter是否為原子操作。
atomic:系統生成的 getter/setter 會保證 get、set 操作的完整性,不受其他線程影響。getter 還是能得到一個完好無損的對象(可以保證數據的完整性),但這個對象在多線程的情況下是不能確定的。
舉個🌰
如果線程 A 調了 getter,與此同時線程 B 、線程 C 都調了 setter——那最后線程 A get 到的值,有3種 可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同時,最終這個屬性 的值,可能是 B set 的值,也有可能是 C set 的值。所以atomic可并不能保證對象的線程安全。
也就是說:如果有多個線程同時調用setter的話,不會出現某一個線程執行完setter全部語句之前,另一個線程開始執行setter情況,相當于函數頭尾加了鎖一樣,每次只能有一個線程調用對象的setter方法,所以可以保證數據的完整性。
atomic所說的線程安全只是保證了getter和setter存取方法的線程安全,并不能保證整個對象是線程安全的。
nonatomic:就沒有這個保證了,nonatomic返回你的對象可能就不是完整的value。因此,在多線程的環境下原子操作是非常必要的,否則有可能會引起錯誤的結果。但僅僅使用atomic并不會使得對象線程安全,我們還要為對象線程添加lock來確保線程的安全。
nonatomic的速度要比atomic的快。atomic是Objc使用的一種線程保護技術,這種機制是耗費系統資源的,所以在iPhone這種小型設備上,我們基本上都是使用nonatomic,而對象的線程安全問題則由程序員代碼控制。
atomic與nonatomic的本質區別其實也就是在setter方法上的操作不同
nonatomic對象、atomic對象setter和getter方法的實現:
/// nonatomic對象
- (void)setCurrentImage:(UIImage *)currentImage
{if (_currentImage != currentImage) {[_currentImage release];_currentImage = [currentImage retain];}
}
- (UIImage *)currentImage
{return _currentImage;
}/// atomic對象
- (void)setCurrentImage:(UIImage *)currentImage
{@synchronized(self) {if (_currentImage != currentImage) {[_currentImage release];_currentImage = [currentImage retain];}}
}- (UIImage *)currentImage
{@synchronized(self) {return _currentImage;}
}
總結:可以發現幾乎所有代碼的屬性設置都會使用nonatomic,這樣能夠提高訪問性能,在iOS中使用鎖機制的開銷較大,會損耗性能。
assign
1.這個修飾詞是直接賦值的意思 , 整型/浮點型等數據類型都用這個詞修飾 .
2.如果沒有使用 weak strong retain copy 修飾 , 那么默認就是使用 assign 了.
3.當然其實對象也可以用 assign 修飾 , 只是對象的計數器不會+1 . ( 與 strong 的區別 )
4.如果用來修飾對象屬性 , 那么當對象被銷毀后指針是不會指向 nil 的 . 所以會出現野指針錯誤 . ( 與weak的區別 )
weak
weak是弱引用,用weak描述修飾或者所引用對象的計數器不會加一,并且會在引用的對象被釋放的時候自動被設置為nil,大大避免了野指針訪問壞內存引起崩潰的情況,另外weak還可以用于解決循環引用。
weak原理概括
weak表其實是一個hash(哈希)表,Key是所指對象的地址,Value是weak指針的地址數組。weak的底層實現的原理是什么?
Runtime維護了一個weak表,用于存儲指向某個對象的所有weak指針。weak表其實是一個hash表,Key是所指對象的地址,value是weak指針的地址(這個地址的值是所指對象指針的地址)數組。
為什么value是數組?因為一個對象可能被多個弱引用指針指向
strong
在ARC環境下,只要某一對象被一個strong指針指向,該對象就不會被銷毀。如果對象沒有被任何strong指針指向,那么就會被銷毀。在默認情況下,所有的實例變量和局部變量都是strong類型的。可以說strong類型的指針在行為上跟非ARC下的retain是比較相似的
copy
淺拷貝
只是將對象內存地址多了一個引用,也就是說,拷貝結束之后,兩個對象的值不僅相同,而且對象所指的內存地址都是一樣的。
深拷貝
拷貝一個對象的具體內容,拷貝結束之后,兩個對象的值雖然是相同的,但是指向的內存地址是不同的。兩個對象之間也互不影響,互不干擾。
@interface 和 @implementation
OC中的類必須包括兩部分,interface部分和implementation部分,這才是oc中的一個類的完整聲明;
OC中將成員變量和成員方法的聲明部分放置在interface部分中,包括繼承關系,protocal實現關系,都在interface里面的頭部進行聲明,
然后將實現部分放置在implementation部分中,相當于是將類拆分成聲明和實現兩部分,這兩部分缺一不可,所以在OC中,不妨不要將interface叫做接口,直接叫做類聲明部分來得容易理解多了,簡而言之,oc中interface是類的一個部分,和implementation共同組成一個完整的類。
- (instancetype)init
這個就是該類的初始化
參考
(instancetype)init{self=[super init];if (self) {}NSLog(@"=========in MyView.init 函數里");return self;
}
+和-
+號是static
self 和 super
self指的是類對象本身;
super是父類對象本身;
self用來調用本類對象的方法;
self關鍵字先從本類中查找是否有此方法,如果沒有,再從父類中調用此方法;
super調用從父類繼承下來的方法;
super關鍵字直接調用父類中定義的方法.
//方法定義
(void)shopping;//不帶參數的方法
-(void)goshopping:(float)price; //帶參數的方法
-(id)someMethod:(int)someArg someOtherArgName:(int)someOtherArg; //otherParameter是參數的別名(第一個參數的別名省略),在函數調用時方便指定。
new、alloc
Object-C中的方法調用形式采用消息發送的方式,通常調用的形式如:
[someObject someMethod:firstArg someOtherArgName:otherArg];
實例的初始化也采用消息發送的形式,可以簡單的調用類型的new方法來獲取一個實例對象,簡單實例化的方法通常是:
someObject *obj = [someObject new]; //類的實例化
new 方法的實際過程是調用 alloc 和 init 方法,因此如果需要采用自定義的方法來初始化實例,則需要自己重寫 init 方法,通常的初始化方式為:
someObject *obj = [[someObject alloc] init]; //采用無參數的init實例化
someObject *obj = [[someObject alloc] initWithArg:Arg]; //采用參數的實例化
調用方法
方法調用的本質就是放對象發送消息
/* new 會調用 init方法 */
People *man = [People new];//類調用new
People *man = [[People alloc] init];//類創建了個對象,調用init//屬性方法調用eat的方式
[man eat];
//類方法調用eat方式
[People eat];
[[People class] eat];//還有一種不常用的調用方式
[對象/類 performSelector:@selector(eat)];//底層實現
objc_msgSend(對象/屬性, @selector(eat));
//_render對象調用registerModule方法,參數是_lookupDescriptor
[_render registerModule:_lookupDescriptor];