《iOS编程实战》读书笔记 24章1.2节
1,方法、属性和协议都存储在类定义的可写字段中,这些信息可以在运行时被改变,这也是分类的实现原理。ivar存储在只读字段,所以不能被修改,这是分类不能添加ivar的原因。
123.png问题: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强制转换,这个还是有点模糊。