1,方法、屬性和協議都存儲在類定義的可寫字段中,這些信息可以在運行時被改變,這也是分類的實現原理。ivar存儲在只讀字段,所以不能被修改,這是分類不能添加ivar的原因。
問題:ivar只讀字段被修改了會造成什么情況,有什么方法還原這種情況?
2,查詢runtime有關的函數,可以查找文檔Objective-C Runtime Reference。
3,打印NSObject能響應的方法列表。
void printObjectMethods(){
unsigned int count = 0;
Method *methods = class_copyMethodList([NSObject class], &count);
for (unsigned int i = 0; i<count; i++) {
SEL sel = method_getName(methods[i]);
const char *name = sel_getName(sel);
printf("%s\n",name);
}
free(methods);
}
</br>
問題:運行時環境沒有引用計數(自動或者手動都沒有),所以沒有等價的retain或release方法。如果從帶有copy的函數得到一個值,就應該調用free。如果用了不到copy單詞的函數,千萬不要調用free。
(緩沖區的概念還需要理解下)
4,模擬消息分配器機制。
static const void *myMsgSend(id receiver,const char *name){
SEL selector = sel_registerName(name);
IMP methodIMP = class_getMethodImplementation(object_getClass(receiver), selector);
return methodIMP;
}
void RunMyMsgSend(){
Class class = (Class)objc_getClass("NSObject");
id object = class_createInstance(class, 0);
myMsgSend(object, "init");
id description = (id)myMsgSend(object, "description");
const char * cstr = myMsgSend(description, "UTF8String");
printf("%s\n",cstr);
}
問題:
(1)當模擬UTF8String的方法時會崩潰,報野指針。
(2)書中顯示:IMP是一個指向某個函數的函數指針,該函數接受一個對象、一個選擇器喝一個可變長參數列表,返回一個對象。但是當使用的時候加入參數會報錯。
5,在Object-C中可以利用methodForSelector:來使用這種技術,從而避開objc_msgSend這個復雜的消息分派器。如果需要在iPhone上對同一個方法調用幾千次,這么做才有意義,而在MAC上,除非調用幾百萬次,否則看不到性能提升。蘋果高度優化了objc_msgSend,但是對于一個調用次數多的建大方法,這么做可以將性能提升5%-10% 。
問題:這種性能提升應該如何測試呢?
6,下面的例子說明如何測試性能。
const NSUInteger kTotalCount = 10000000;
typedef void (*voidImp)(id,SEL,...);
void FastCall(){
NSMutableString *string = [NSMutableString string];
NSTimeInterval totalTime = 0;
NSDate *start = nil;
NSUInteger count = 0;
//用objc_msgSend
start = [NSDate date];
for (count = 0; count<kTotalCount; count++) {
[string setString:@"stuff"];
}
totalTime = -[start timeIntervalSinceNow];
printf("w/ objc_msgSend = %f\n",totalTime);
//跳過objc_msgSend
start = [NSDate date];
SEL selector = @selector(setString:);
voidImp setStringMethod = (voidImp)[string methodForSelector:selector];
for (count = 0; count<kTotalCount; count++) {
setStringMethod(string,selector,@"stuff");
}
totalTime = -[start timeIntervalSinceNow];
printf("w/o objc_msgSend = %f\n",totalTime);
}
總結:
因為IMP返回id,ARC會保留返回值,之后再釋放。不過,這個方法什么都沒有返回。這個開銷會比普通的消息傳遞系統大,有些情況下多余的retain還會造成崩潰,這就是我們要添加額外的voidIMP的原因。通過把setStringMethod函數指針聲明為返回void,編譯器就會跳過retain。
問題:
(1)函數指針與指針函數概念是什么?
函數指針:指向函數的指針。
指針函數:返回值是指針的函數。
以下內容來自互聯網資料:
在指針函數中,有這樣一類函數,它們也返回指針,但是這個指針不是指向int、char之類的基本類型,而是指向函數。比如,下面的語句:
int (*ff(int))(int *, int);
我們用上面介紹的方法分析一下,ff首先與后面的“()”結合,即:
int (*(ff(int)))(int *, int); // 用括號將ff(int)再括起來
也就意味著,ff是一個函數。
接著與前面的“*”結合,說明ff函數的返回值是一個指針。然后再與后面的“()”結合,也就是說,該指針指向的是一個函數。
一般來說,用typedef關鍵字會使該聲明更簡單易懂。
int (*PF)(int *, int);
也就是說,PF是一個函數指針“變量”。當使用typedef聲明后,則PF就成為了一個函數指針“類型”,即:
typedef int (*PF)(int *, int);
這樣就定義了返回值的類型。然后,再用PF作為返回值來聲明函數:
PF ff(int);
在通俗一點,就是可以將函數名直接賦值給函數指針,應該就是讓函數指針指向此函數,調用的時候用函數指針調用就可以。
pFunc p_func; //此處定義了一個函數指針
p_func = count1; //把count1函數的地址賦值給p_func
int count1(char *p) {
...
}
(2)代碼中進行了voidIMP強制轉換,這個還是有點模糊。