iOS开发专题iOS Developer

《iOS编程实战》读书笔记 24章1.2节

2016-08-12  本文已影响43人  方振

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强制转换,这个还是有点模糊。

上一篇下一篇

猜你喜欢

热点阅读