美文网首页
2.对象、消息、运行期

2.对象、消息、运行期

作者: 开发者老岳 | 来源:发表于2017-05-12 11:28 被阅读19次

    第6条 理解属性这一概念


    以上也是Category 为何不能添加成员变量的原因,更多参考:
    http://quotation.github.io/objc/2015/05/21/objc-runtime-ivar-access.html
    http://www.jianshu.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也会跟着修改,
        //但开发者可能会认为 self.name 只是个 NSString 的不可变字符串,不应该被修改,但其实却是个 NSMutableString。
        //若用了copy就不会发生这种情况。
    }
    
    
    • 如果属性用 copy 修饰了,在 init 方法里要对应的用 copy 赋值,如:
    @property (copy) NSString *name;
    
    - (instancetype)initWithName:(NSString *)newName
    {
        self = [super init];
        if (self) {
            _name = [newName copy]; //要用copy赋值
        }
        return self;
    }
    
    
    • 如果设置了 readonly ,可以只在 init 方法里赋值(如下代码),其他地方再调用 self.name = xxx 赋值的话,就会报错了。
    @property (copy, readonly) NSString *name;
    
    - (instancetype)initWithName:(NSString *)newName
    {
        self = [super init];
        if (self) {
            _name = [newName copy];
        }
        return self;
    }
    
    • atomic 和 nonatomic 的区别


    第7条 在对象内部尽量直接访问成员变量

    • 在对象内部,读取的时候尽量用成员变量(惰性初始化除外,如下代码),赋值的时候尽量用属性。
      原因:
      1. 读取时,直接访问成员变量,速度会比较快,
      2. 但是如果赋值时,如果直接访问成员变量就绕过了内存管理机制,比如 copy 修饰了一个属性,若直接给成员变量赋值,就会 retain 而不会 copy 了。
      3. 直接访问成员变量,不会触发 KVO。
      4. 用属性,方便在 setter 方法里打断点调试。
    • 在 init 和 dealloc 中,尽量直接使用成员变量读写数据。
    // 惰性初始化
    - (Student *)student 
    {
        if (!_ student) {
            _ student = [Student new];
        }
        return _ student;
    }
    

    第8条 理解“对象等同性”这个概念

    NSObject 的判断俩对象是否相同的方法 isEqual:- (NSUInteger)hash 的关系:
    若用 isEqual: 判断俩对象相等,则 hash 一定相同,但 hash 相同,isEqual: 判断不一定相等。

    • hash函数如何编写(未读懂)
    • NSArray 调用 isEqual: 的实现原理: 先判断个数是否相同,若相同,每一项调用isEqual:判断是否相同。
    • NSSet (集合)是一种哈希表,运用散列算法,查找集合中的元素比数组速度更快,但是它没有顺序。集合中的数据是唯一的,比如我存入俩相同的字符串,集合里只会有一个。

    第9条 以“类族(类簇)模式”隐藏实现细节

    类族一般没有 init 方法,只有一些工厂方法,它只是一个抽象的基类,中间的实现过程都隐藏了。例如UIButton,初始化没有init,而是用 buttonWithType:,其内部可能是每个类型声明了一个新的类(只是举个UIButton的例子,UIButton实际并不是类族)。
    collection类(集合类)一般都是类族,包括 NSArray、NSSet和NSDictionary等,NSString、NSNumber等也是类族,以下结果返回的是NO:

    NSArray *arr = [NSArray arrayWithObjects:@"abc", nil];
      if ([arr isMemberOfClass:[NSArray class]])
      {
          NSLog(@"YES");
      }
      else {
          NSLog(@"NO");
      }
    

    第10条 在既有类中使用关联对象存储自定义数据

    #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 */
    };
    
    //设置关联
    void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) 
    
    //获取关联的对象值
    id objc_getAssociatedObject(id object, const void *key)
    
    //移除指定对象的全部关联对象
    void objc_removeAssociatedObjects(id object)  
    

    UIAlertView 的 delegate 传值的时候,如果传一个 tag 还好说,如果是传多个参数,要么用一个子类继承于 UIAlertView,设置多个属性,要么就用关联对象。

    第11条 理解 objc_msgSend的作用

    C语言是静态绑定,若不考虑内联(inline),当你调用一个函数的时候,编译的时候编译器就知道存在这个函数了。

    // OC 中的语句
    id retureValue = [obj messageName:parameter];
    
    // 转成C语言后的原型
    id retureValue = objc_msgSend(obj, @selector(messageName:), parameter);
    

    objc_msgSend 方法会在 接收者(obj)中寻找方法列表,如果找不到,就在从父类依次往上找,一直找到NSObject,如果还没找到,就开始消息转发。这个过程中,objc_msgSend并非每次都会查找,每个类会有一个缓存,这样大大提高了查找效率。

    objc_msgSend_stret
    objc_msgSend_fpret
    objc_msgSendSuper
    ...
    还有些内容太抽象,此处省略。

    第12条 理解消息转发机制

    上条介绍的消息传递,传递过程中找不到对应的方法,就会执行消息转发。消息转发分为两大阶段:

    1. 动态解析:询问接收者所属的类,能否动态添加方法。
    2. 完整的消息转发:系统请求接收者用其他方式处理消息。又分为俩阶段(就是俩回调方法),首先,先看下其他类能否处理该消息,若没有,系统会把与消息有关的全部细节封装给NSInvocation对象中,再给接收者,最后一次机会。

    代码示例,建了一个EOCAutoDictionary的类,可以用该类任意存值。如:

        ECOAutoDictionary *a = [ECOAutoDictionary new];
        a.data = [NSDate date];
        NSLog(@"a.data-------%@", a.date);
    

    第13条 用“方法调配技术”调试“黑盒方法”

    method swizzling,一般用于打印一个系统方法日志,最好不要滥用,否则会让代码不易被读懂。

    第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 的作用:负责将消息转发到真正的target的代理类。举个例子,你想要卖一件二手物品,但是你并不想直接跟卖家接触(直接向target发消息),这时你去找了一个第三方,你告诉这个第三方你要买什么、出多少钱买、什么时候要等(向代理发消息),第三方再去跟卖家接触并把这些信息转告卖家(转发消息给真实的target),最后通过第三方去完成这个交易。

    相关文章

      网友评论

          本文标题:2.对象、消息、运行期

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