2.对象、消息、运行期

第6条 理解属性这一概念

2.对象、消息、运行期_第1张图片
2.对象、消息、运行期_第2张图片
2.对象、消息、运行期_第3张图片

2.对象、消息、运行期_第4张图片

以上也是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 的区别


    2.对象、消息、运行期_第5张图片

第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 

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)  
2.对象、消息、运行期_第6张图片

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

第11条 理解 objc_msgSend的作用

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

2.对象、消息、运行期_第7张图片
// 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对象中,再给接收者,最后一次机会。
2.对象、消息、运行期_第8张图片

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

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

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

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

第14条 理解“类对象”的用意

NSObject 类在 oc 中的定义是:

@interface 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 的类方法就是在元类里定义的。

2.对象、消息、运行期_第9张图片

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

你可能感兴趣的:(2.对象、消息、运行期)