美文网首页iOS开发专题iOS Developer
《iOS编程实战》读书笔记 24章1.2节

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

作者: 方振 | 来源:发表于2016-08-12 11:01 被阅读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强制转换,这个还是有点模糊。

    相关文章

      网友评论

        本文标题:《iOS编程实战》读书笔记 24章1.2节

        本文链接:https://www.haomeiwen.com/subject/jxossttx.html