美文网首页
Effective Objective 2.0 读书笔记(更新完

Effective Objective 2.0 读书笔记(更新完

作者: sdupidBoby | 来源:发表于2016-08-14 18:29 被阅读109次

    (*useful)标记:目前觉得有用的函数
    //FIXME 标记:待补充

    第一章:

    1. OC起源
     CGRect 这种结构体都存在栈上
     OC对象在堆上(无需手动管理ARC)
    
    2. 在类的头文件尽量少引用其他头文件
     增加编译时间,增加类之间的耦合性(引用头文件不回增加app包的大小)
    
    3.尽量使用语法糖
    dic[@"key"]
    更加简洁,直接跑出异常(创建时避免nil)
    
    4.多使用类型常量,少用#define
    define 定义出来没有类型,例:预处理会把所有的定义ANIMATION_DURATION都替换成0.3
    static const  申明内部静态变量
    extend 作为外部引用(命名:类名打头,这种常量会放在“全局符号表 global symbol table”)
    
    5. 用枚举表示状态,选项码
    typedef NS_ENUM 
    typedef NS_OPTIONS  使用位由当前硬件设备iphone5s 以上64bit
    语法清晰,自定义类型
    多个选项同时使用,按位或操作将其组合起来
    

    第二章:

    6.理解属性
    使用nonatomic
    natomic会使用同步锁,增加性能开销,但并不能保证线程安全
    
    7.在对象内部尽量直接访问实例变量 _property
    通过"惰性初始化"(创建成本高,不常用)的属性,必须使用 属性 来访问
    在对象内部:
        1. 读取数据  直接(_ )
        2. 写入数据  属性
    dealloc中直接通过 实例变量 来读写数据 (_ )
    
    8.对象等同性
    1.  应该比较的是两个对象的指针
    2.  哈希码有可能相同,对象却不同
    3.  编写hash时,尽量使用速度快而且hash碰撞几率低的算法
    
    9.使用类族隐藏实现细节
    1. 类族模式可以把实现细节隐藏在一套简单的公共接口后面
    2. cocoa中的集合类型 多使用 类族
    类族:+ (instancetype)buttonWithType:(UIButtonType)buttonType;
    
    10.在既有类中使用关联对象存放自定义数据
    1.类似KV的形势进行关联对象(OBJC_ASSOCIATION_COPY指定属性)
    2.会引入(retian cycle)-->关联alert使用block回调的情况下
    3. 尽量少用 (runtime 在实际编程中应用都不会太多)
    
    11.消息传递. objc_msgSend
    1. 运行时会把OC的方法转换成 void setter(id self, SEL _cmd ,id newValue)
    2. objc_send 会把 调用的方法 放置到“快速映射表”(fast map)
    
    12.消息转发机制

    2018.3.6更新:

    • 在接收到触摸事件后,会先在内存缓存中查找是否存在该实例方法。

    如果在方法接受类的“list of methods”没有找到对应的方法,就继续向上查找,最终找不到就会启动 消息转发

      1.查找接收者所属的类,看其是否能动态添加方法,以处理这个“未知的方法”。(动态方法解析)
        +(BOOL) resolveInstanceMethod:(SEL)selector
      2.运行期系统把消息转给其他接收者处理。(备援接收者)
        -(id)forwardingTargetForSelector:(SEL)selector
      3.经过上述两步后,如果还是没有办法处理选择子,就启动完成的消息转发
        -(void)forwardInvocation:(NSInvocation *)invocation
    
    消息转发.png
    13.method swizzling
    +(void)load
    {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(systemFontOfSize:);
        SEL swizzledSelector = @selector(XMTsystemFontOfSize:);
        
        Method originalMethod = class_getClassMethod(class, originalSelector);
        Method swizzledMethod = class_getClassMethod(class, swizzledSelector);
        
        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) { //这个添加方法不应该成功(暂时没去Debug)
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
    }
    + (UIFont *)XMTsystemFontOfSize:(CGFloat)fontSize{
          return [self XMTsystemFontOfSize:(((fontSize)/375.0)* CGRectGetWidth([UIScreen mainScreen].bounds))];
     }   
    
    • 这个东西不能乱用
    14.理解"类对象"
    typedef struct objc_class *Class;
    struct objc_class {
           Class isa;
           Class super_class;
           const char *name;
           long version;
           long info;
           long instance_size;
           struct objc_ivar_list *ivars;
    
    #if defined(Release3CompatibilityBuild)
          struct objc_method_list *methods;
    #else
         struct objc_method_list **methodLists;
    #endif
        struct objc_cache *cache;
        struct objc_protocol_list *protocols;
      };
    
    • 解释:该结构体存放类的“元数据”。其中,isa指针定义了另外一个类——元类(metaclass),用来表述类对象本身所具备的元数据。super_class定义了本类的超类
      盗图isa哈哈.png
    1. 每个实例都有一个指向Class对象的指针,用以表明其类型,而这些Class对象则构成了类的继承体系。
    • 如果对象类型无法在编译期确定,那么就应该使用类型信息查询方法(内省)来探知。
    • 尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。

    补充:runtime的setIvar会将操作的Ivar可变属性重置为readValue (*useful)

      const char * name = [@"_name" UTF8String];
      Ivar nameIvar = class_getInstanceVariable([self class], name);
      if (nameIvar && readValue) {
         object_setIvar(self, nameIvar, readValue); //重置对象地址
      }
    
    (*useful)类似对数组的操作 filter(), concat() 和 slice(),会返回一个新的数组
    

    第三章:接口与API设计

    15.用前缀避免命名空间冲突
    * 选择与你的公司、应用程序或二者皆有关联之名作为类名的前缀,并在所有代码中均使用这一前缀。
    * 若自己所开发的程序库中用到了第三方库,则应为其中的名称加上前缀。
    
    16.提供"全能初始化方法"
    • 在类中提供一个全能初始化方法,并于文档里指明。其他初始化方法均调用此方法。
    • 若全能初始化方法与超类不同,则需覆写超类中的对应方法。
    • 如果超类的初始化方法不适用于子类,那么应该覆写这个超类方法,并在其中抛出异常
    第17条:实现description方法
     这个没什么好说的!方便debug(插件:AutoGenerateDescriptionPluginProd)
    
    第18条:尽量使用不可变对象

    KVC:任何私有,不可变对象都可以间接的通过这种方式修改

    • 尽量创建不可变的对象。( readonly)
    • 若某属性仅可于对象内部修改,则在“class-continue分类”(27条)中将其由readonly属性扩展为readwrite属性
    • 不要可变的collection作为属性公开,而应提供相关方法,以此修改对象中的可变collection。
    第19条:使用清晰而协调的命名方式
    • 驼峰式命名
    第20条:为私有方法名加前缀
     - (void)publicMethod{ /* --- */} 
     - (void)p_privateMethod{ /* --私有方法前面加 P_-- */}
    
    第21条:理解Objective-C错误模型
    • NSError对象里会封装3条信息:
      Error domain:错误范围,其类型为字符串。通常用一个特有的全局常量来定义。
      Error code:错误码,其类型为整数。通常定义为枚举类型。
      User info:用户信息,其类型为字典。

    • 只有发生了可使整个应用程序崩溃的严重错误时,才应使用异常。

    • 在错误不那么严重的情况下,可以指派“委托方法”(delegate method)来处理错误,也可以把错误信息放在NSError对象里,经由“输出参数”返回给调用者。

    第22条:理解NSCopying协议
    • 要点
      1. 若想令自己所写的对象具有拷贝功能,则需实现NSCopying协议。
      2. 如果自定义的对象分为可变版本与不可变版本,那么就要同时实现NSCopying与NSMutableCopying协议。
      3. 复制对象时需决定采用浅拷贝还是深拷贝,一般情况下应该尽量执行浅拷贝。
      4. 如果你所写的对象需要深拷贝,那么可考虑新增一个专门执行深拷贝的方法。

    第4章.协议与分类

    第23条:通过委托与数据源协议进行对象间通信
    这个没有什么说的详见UITableView (Api)
    
    第24条:将类的实现代码分散到便于管理的数个分类之中
     将独立的功能采用类目的方式进行封装如下(封装的思想)
    

    @interface CALayer (XMT)
    -(void)setBorderColorFromInterFaceBuilder:(UIColor  *)Color;
    @end
    @implementation CALayer (XMT)
    -(void)setBorderColorFromInterFaceBuilder:(UIColor *)Color{
    self.borderColor = Color.CGColor;
    }  
    @end
    
    第25条:总是为第三方类的分类名称加前缀
    降低耦合行
    
    第26条:勿在分类中声明属性
    前面说过分类是用来 拆分功能模块(高类聚低耦合,模块化功能)
    
    第27条:使用 "class-cotinuation"分类隐藏实现细节
    @interface :
    @property(no,stro,readonly)
    @interface ()
    @property(no,stro,readwrite)
    
         通过“class-continuation分类”向类中新增实例变量。
         如果某属性在主接口中声明为“只读”,而类的内部又要用设置方法修改此属性,那么就在““class-continuation分类”中将其扩展为“可读写”。
         把私有方法的原型声明在““class-continuation分类”里面。
         若想使类所遵循的协议不为人所知,则可于““class-continuation分类”中声明。
    
    第28条:通过协议隐藏匿名对象
    协议可在某种程度上提供*匿名类型*。具体的对象类型可以淡化成*遵从某协议的id类型*,协议里规定了对象所实现的方法。
    使用匿名对象来隐藏*类型名称*(或*类名*)。
    如果具体类型不重要,重要的是对象能够响应(定义在协议里的)特定方法,那么可使用匿名对象来表示。    
    

    第5章.内存管理

    第29条: 引用计数

    这个基本是iOS的入门基础

    第30条: 以ARC简化引用计数
    • ARC只负责OC对象的内存 尤其要注意:CoreFoundation对象不归ARC管理,开发者必须适时调用CFRetain/CFReleas
    strong 保留此值
    unsafe_unretained 不保持此值,做么做可能不安全,因为等到再次使用变量时,其对象可能已经回收了
    weak 不保留此值,但是变量可以安全使用,因为如果系统把这个对象回收了,那么变量也会自动清空
    autoreleasing 把对象"按引用传递"(pass by reference) 给方法时,使用这个特殊的修饰符,此值在方法返回式自动释放
    
    
    第31条: 在dealloc方法中只释放引用并解除监听
    * dealloc中 不要使用属性(因为已经释放了)
    * 释放KVO  NSNoficationCenter
    * dealloc中不要去执行异步操作
    
    第32条: 编写"异常安全代码"时留意内存管理问题
    哪里都需要注意内存问题,都应该重写dealloc去Debug
    
    第33条: 以弱引用避免保留环
    @weakify(self)
    __weak typeof(self) weakSelf = self;
    blockCallback^{
      self.property
      self->property
      self->_property
      @strongify(self); //强持有,避免在引用时释放
    }
    
    第34条: 以"自动释放池"降低内存峰值
    for(int i = 0; i < 10000; i ++){
       @autoreleasepool{
           这里创建大量对象
       }
    }
    
    
    第35条: 用"僵尸"对象 调试内存问题

    工具:

    1. instrument
    2. facebook的 FBMemoryProfiler
    3. Zombie



    第36条: 不要使用retainCount
    retainCount这个东西 也不一定准确
    - (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;
    

    第6章.块与大中枢派发

    第37条: 理解 "块"
    C C++ OC OC++中的词法闭包
    
    栈: void(^block)()   栈上的内存会在复用时被覆盖(unsafe),使用Copy拷贝到 堆(内存管理,引用计数)
    堆(常用):copy
    
    typedef void (^Block)();
    @property (nonatomic, copy) id (^Block)(id value);
    int(^block)() = ^{
            return 1;
        };
    
    第38条: 为常用的块类型创建typedef
    typedef id  (^Block)(id value);
    1. 以typedef重新定义块类型,可令块变量使用起来更加简单
    2.复用
    
    第39条: 用handler块降低代码分散程度
    1.代理模式( delegate)的回调会比较的分散(如果没有良好的变成习惯就更难受了)
    2.iOS的回调逐渐开始普及Block(UIAlertViewController)
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^(){
            for (int i = 0; i < 10; i++) {
                NSLog(@"执行并发队列 :%d",i);
            }
        }];
    
    第40条:用块引用其属性对象时不要出现保留环

    FBRetainCycleDetecto

    @weakify(self)
    __weak typeof(self) weakSelf = self;
    blockCallback^{ 
       self.property 
       self->property
       self->_property 
       @strongify(self); //强持有,避免在引用时释放
    }
    
    第41条:多用派发队列,少用同步锁
    1. @synchronized 同步锁 效率低
    2. NSLock(tryLock)      NSRecursiveLock (支持递归)
    
    3.派遣信号
    dispatch_semaphore_t semaphonre = dispatch_semaphore_create(1);
    dispatch_semaphore_wait(semaphonre, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_signal(semaphonre);
    
    4.dispatch_barrier 没错使用它也可以做到这一点
        dispatch_queue_t queue = dispatch_queue_create("my.lable", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:3];
            NSLog(@"1");
        });
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:2];
            NSLog(@"2");
        });
        //前面执行后执行自身,然后执行后面
        dispatch_barrier_async(queue, ^{
            [NSThread sleepForTimeInterval:3];
            NSLog(@"3");
        });
        dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:1];
            NSLog(@"4");
        });
    
    第42条:多用GCD少用performSelector系列方法
    1.performSelector系列方法在内存管理方面容易有疏失,它无法确定将要执行的选择子具体是什么,因而ARC编译器也就无法插入适当的内存管理方法
    2.performSelector系列方法所能处理的选择子太过局限,选择子的返回值类型及发送给方法的参数个数都受到限制
    3.使用GCD代替(after dispatch_async)
    
    第43条:掌握GCD及操作队列的使用时机
    OperationQueue从4.0开始就是 基于GCD了,背后的线程池已经降低了开线程带来的额外开销。
    更何况一般的应用场景都会设置最大并发数,一个或者几个线程在运行没有太多性能或资源上的差异。
    真正的原因是从后台触发一个异步NSOperation的时候,
    需要有一个在运行的runloop接收NSURLConnection的回调(cancle)。仅此而已。
    
    第44条:通过Dispatch group机制根据系统资源状态来执行任务
    第45条:通过Dispatch_once 来执行只需运行一次的线程安全代码
    第46条:不要使用dispatch_get_current_queue
    多层线程套用会产生死锁
    

    第7章.系统框架

    第47条:熟悉系统框架
    Foundation: 所有OC程序的基础,包含类似NSObject这些基类
    coreFoundation:Foundation的C实现,通过(toll-free bridging)五丰桥接
    CFNetworking:提供网络通信能力
    ....
    
    第48条:多用块枚举,少用for循环

    在遍历dic,set时更加高效

    [Arr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            
    }];
    
    第49条:对自定义其内存管理语义的collection使用无缝桥接
    NSArray * anNSArr = @[@1,@2,@3,@4];
    CFArrayRef aCFArr = (__bridge CFArrayRef)anNSArr;
    NSLog(@"size of Array = %li", CFArrayGetCount(aCFArr));
    
    
    第50条:构建缓存时选用NScache,而非NSDictionay
    NSCache会在系统将要耗尽时(memory warning)他会自动删减缓存,线程安全的
    
    第51条:精简initilalize与load的实现代码
    对于加入运行期系统中的每个类(class)及分类(category)来说,必定会调用此方法,而且仅调用一次。
    + (void)load {
      //这里面不能调其他类(其他类可能还没加载,不安全)
    }
    
    程序首次调用该类之前调用,而且只调用一次
    + (void)initialize{
    
    }
    
    第52条:别忘了NSTimer会保留其目标对象
    timer会循环引用,这是常识了
    

    相关文章

      网友评论

          本文标题:Effective Objective 2.0 读书笔记(更新完

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