第6條 理解屬性這一概念
以上也是Category 為何不能添加成員變量的原因逾滥,更多參考:
http://quotation.github.io/objc/2015/05/21/objc-runtime-ivar-access.html
http://www.reibang.com/p/2d63477c4d46
__unsafe_unretained 跟 assign 類似,只不過它是修飾對象的败匹,而 assign 是修飾基本類型的匣距。兩者修飾的屬性在釋放時,都不會自動將屬性值置為 nil哎壳。
修飾NSString 的時候最好用 copy,原因如下:
@property (retain) NSString *name;
- (void)xxx
{
NSMutableString *mName = [@"mName" mutableCopy];
self.name = mName;
//接下來尚卫,如果我修改了 mName(比如在 mName上拼字符串)归榕,那么self.name也會跟著修改,
//但開發(fā)者可能會認(rèn)為 self.name 只是個 NSString 的不可變字符串吱涉,不應(yīng)該被修改刹泄,但其實卻是個 NSMutableString外里。
//若用了copy就不會發(fā)生這種情況。
}
- 如果屬性用 copy 修飾了特石,在 init 方法里要對應(yīng)的用 copy 賦值盅蝗,如:
@property (copy) NSString *name;
- (instancetype)initWithName:(NSString *)newName
{
self = [super init];
if (self) {
_name = [newName copy]; //要用copy賦值
}
return self;
}
- 如果設(shè)置了
readonly
,可以只在init
方法里賦值(如下代碼)姆蘸,其他地方再調(diào)用self.name = xxx
賦值的話墩莫,就會報錯了。
@property (copy, readonly) NSString *name;
- (instancetype)initWithName:(NSString *)newName
{
self = [super init];
if (self) {
_name = [newName copy];
}
return self;
}
-
atomic 和 nonatomic 的區(qū)別
第7條 在對象內(nèi)部盡量直接訪問成員變量
- 在對象內(nèi)部逞敷,讀取的時候盡量用成員變量(惰性初始化除外狂秦,如下代碼),賦值的時候盡量用屬性推捐。
原因:- 讀取時裂问,直接訪問成員變量,速度會比較快牛柒,
- 但是如果賦值時堪簿,如果直接訪問成員變量就繞過了內(nèi)存管理機(jī)制,比如 copy 修飾了一個屬性皮壁,若直接給成員變量賦值椭更,就會 retain 而不會 copy 了。
- 直接訪問成員變量闪彼,不會觸發(fā) KVO甜孤。
- 用屬性,方便在 setter 方法里打斷點調(diào)試畏腕。
- 在 init 和 dealloc 中缴川,盡量直接使用成員變量讀寫數(shù)據(jù)。
// 惰性初始化
- (Student *)student
{
if (!_ student) {
_ student = [Student new];
}
return _ student;
}
第8條 理解“對象等同性”這個概念
NSObject 的判斷倆對象是否相同的方法 isEqual:
和 - (NSUInteger)hash
的關(guān)系:
若用 isEqual:
判斷倆對象相等描馅,則 hash
一定相同把夸,但 hash
相同,isEqual:
判斷不一定相等铭污。
- hash函數(shù)如何編寫(未讀懂)
-
NSArray
調(diào)用isEqual:
的實現(xiàn)原理: 先判斷個數(shù)是否相同恋日,若相同,每一項調(diào)用isEqual:
判斷是否相同嘹狞。 -
NSSet
(集合)是一種哈希表岂膳,運用散列算法,查找集合中的元素比數(shù)組速度更快磅网,但是它沒有順序谈截。集合中的數(shù)據(jù)是唯一的,比如我存入倆相同的字符串,集合里只會有一個簸喂。
第9條 以“類族(類簇)模式”隱藏實現(xiàn)細(xì)節(jié)
類族一般沒有 init 方法毙死,只有一些工廠方法,它只是一個抽象的基類喻鳄,中間的實現(xiàn)過程都隱藏了扼倘。例如UIButton,初始化沒有init除呵,而是用 buttonWithType:
再菊,其內(nèi)部可能是每個類型聲明了一個新的類(只是舉個UIButton的例子,UIButton實際并不是類族)竿奏。
collection類(集合類)一般都是類族袄简,包括 NSArray、NSSet和NSDictionary等泛啸,NSString绿语、NSNumber等也是類族,以下結(jié)果返回的是NO:
NSArray *arr = [NSArray arrayWithObjects:@"abc", nil];
if ([arr isMemberOfClass:[NSArray class]])
{
NSLog(@"YES");
}
else {
NSLog(@"NO");
}
第10條 在既有類中使用關(guān)聯(lián)對象存儲自定義數(shù)據(jù)
#import <objc/runtime.h>
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /** 和 assgin 等效 */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /** strong候址、nonatomic */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /** copy吕粹、nonatomic */
OBJC_ASSOCIATION_RETAIN = 01401, /** strong、atomic */
OBJC_ASSOCIATION_COPY = 01403 /**< copy岗仑、atomic */
};
//設(shè)置關(guān)聯(lián)
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
//獲取關(guān)聯(lián)的對象值
id objc_getAssociatedObject(id object, const void *key)
//移除指定對象的全部關(guān)聯(lián)對象
void objc_removeAssociatedObjects(id object)
UIAlertView 的 delegate 傳值的時候匹耕,如果傳一個 tag 還好說,如果是傳多個參數(shù)荠雕,要么用一個子類繼承于 UIAlertView稳其,設(shè)置多個屬性,要么就用關(guān)聯(lián)對象炸卑。
第11條 理解 objc_msgSend的作用
C語言是靜態(tài)綁定既鞠,若不考慮內(nèi)聯(lián)(inline),當(dāng)你調(diào)用一個函數(shù)的時候盖文,編譯的時候編譯器就知道存在這個函數(shù)了嘱蛋。
// OC 中的語句
id retureValue = [obj messageName:parameter];
// 轉(zhuǎn)成C語言后的原型
id retureValue = objc_msgSend(obj, @selector(messageName:), parameter);
objc_msgSend 方法會在 接收者(obj)中尋找方法列表,如果找不到五续,就在從父類依次往上找洒敏,一直找到NSObject,如果還沒找到疙驾,就開始消息轉(zhuǎn)發(fā)凶伙。這個過程中,objc_msgSend并非每次都會查找它碎,每個類會有一個緩存镊靴,這樣大大提高了查找效率铣卡。
objc_msgSend_stret
objc_msgSend_fpret
objc_msgSendSuper
...
還有些內(nèi)容太抽象,此處省略偏竟。
第12條 理解消息轉(zhuǎn)發(fā)機(jī)制
上條介紹的消息傳遞,傳遞過程中找不到對應(yīng)的方法敞峭,就會執(zhí)行消息轉(zhuǎn)發(fā)踊谋。消息轉(zhuǎn)發(fā)分為兩大階段:
- 動態(tài)解析:詢問接收者所屬的類,能否動態(tài)添加方法旋讹。
- 完整的消息轉(zhuǎn)發(fā):系統(tǒng)請求接收者用其他方式處理消息殖蚕。又分為倆階段(就是倆回調(diào)方法),首先沉迹,先看下其他類能否處理該消息睦疫,若沒有,系統(tǒng)會把與消息有關(guān)的全部細(xì)節(jié)封裝給NSInvocation對象中鞭呕,再給接收者蛤育,最后一次機(jī)會。
代碼示例葫松,建了一個EOCAutoDictionary的類瓦糕,可以用該類任意存值。如:
ECOAutoDictionary *a = [ECOAutoDictionary new];
a.data = [NSDate date];
NSLog(@"a.data-------%@", a.date);
第13條 用“方法調(diào)配技術(shù)”調(diào)試“黑盒方法”
method swizzling腋么,一般用于打印一個系統(tǒng)方法日志咕娄,最好不要濫用,否則會讓代碼不易被讀懂珊擂。
第14條 理解“類對象”的用意
NSObject
類在 oc 中的定義是:
@interface NSObject <NSObject> {
Class isa OBJC_ISA_AVAILABILITY;
}
typedef struct objc_class *Class;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
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;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
舉個例子:
NSString *str = @"abc";
看下面的圖的定義圣勒,在這個例子中,str 的 isa 指針是指向 NSString摧扇,而 NSString 也有個 isa 指針圣贸,這個指針指向的類就是 元類,說明 NSString 是該元類的一個實例對象扳剿。NSString 的類方法就是在元類里定義的旁趟。
NSProxy
簡單用法:http://ios.jobbole.com/87856/NSProxy
的作用:負(fù)責(zé)將消息轉(zhuǎn)發(fā)到真正的target的代理類。舉個例子庇绽,你想要賣一件二手物品锡搜,但是你并不想直接跟賣家接觸(直接向target發(fā)消息),這時你去找了一個第三方瞧掺,你告訴這個第三方你要買什么耕餐、出多少錢買、什么時候要等(向代理發(fā)消息)辟狈,第三方再去跟賣家接觸并把這些信息轉(zhuǎn)告賣家(轉(zhuǎn)發(fā)消息給真實的target)肠缔,最后通過第三方去完成這個交易夏跷。