美文网首页
《Effective Objective-C 2.0》读书笔记.

《Effective Objective-C 2.0》读书笔记.

作者: action爱生活 | 来源:发表于2018-05-09 18:05 被阅读17次

    [TOC]

    多用类型常量,少用 #define

    1. 对于局部常量(.m文件中),
    • 使用 static 声明表明变量只在本文件中可见,所以无需前缀
    • 同时使用 static const 关键字与#define 效果相同,好处在于带有类型信息。
    static const CGFloat kTopViewHeight = 40;
    
    1. 对于全局常量
    • 由于全局使用,使用类名做前缀
    • 在 .h 文件中使用 extern 声明
    extern NSString * const KKSlideTabBarBgColor;
    
    • 在 .m 文件中定义
    NSString * const KKSlideTabBarBgColor = @"name";
    

    NS_ENUM 与 NS_OPTIONS

    • 使用 NS_ENUMNS_OPTIONS 可以指定底层数据类型,而且可以保证系统兼容
    • 当多种状态可以互相组合时,使用 NS_OPTIONS,否则使用 NS_ENUM
    • 命名规则:前缀+状态
    typedef NS_ENUM(NSUInteger, GDFConnectionState) {
        GDFConnectionStateDisconnected,
        GDFConnectionStateConnecting,
        GDFConnectionStateConnected,
    };
    
        GDFConnectionStateDisconnected,
        GDFConnectionStateConnecting,
        GDFConnectionStateConnected,
    };
    
    typedef NS_OPTIONS(NSUInteger, GDFDirection) {
        GDFDirectionUp    = 1 << 0,
        GDFDirectionDown  = 1 << 1,
        GDFDirectionLeft  = 1 << 2,
        GDFDirectionRight = 1 << 3,
    };
    

    消息转发 message forwarding

    e28856e7825cc2b7113288d11b2f1be6.png

    动态方法解析 resolve method

    • 动态方法解析是消息转发的第一步,在这里处理,效率最高
      @dynamic 属性 使编译器不自动生成实例变量及存储方法
      调用的方法:
    + (BOOL)resolveInstanceMethod:(SEL)selector
    

    代码参见:Runtime.md 动态方法解析 resolve method

    方法调配 method swizzling

    1. 作用1:在运行死交换两个方法的实现
    // 根据方法名找到方法的实现
    class_getInstanceMethod(__unsafe_unretained Class cls, SEL name)
    // 交换两个方法的实现
    method_exchangeImplementations(Method m1, Method m2)
    
    1. 作用2:为既有的方法实现添加新功能——调试黑盒方法,为完全不知道具体实现的方法添加日志功能


      fed427a97a343ee16f2c5edc37065689.png
    @implementation NSString (EOC)
    
    + (void)load {
        Method originalMethod = class_getInstanceMethod([NSString class],
                                                        @selector(lowercaseString));
        Method swappedMethod = class_getInstanceMethod([NSString class],
                                                       @selector(eoc_myLowercaseString));
        method_exchangeImplementations(originalMethod, swappedMethod);
    }
    
    - (NSString *)eoc_myLowercaseString {
        NSString *lowercase = [self eoc_myLowercaseString];
        NSLog(@"%@ => %@", self, lowercase);
        return lowercase;
    }
    

    类对象

    // 对象结构体
    // isa 指针指向类对象
    struct objc_object {
        Class isa;
    };
    
    // 类结构体
    // 1. 这个结构存放类的元数据,实例中的方法,变量等信息就存储在类对象中
    // 2. isa 指针指向元类(metaclass),元类描述类对象本身所具有的元数据,类方法就存储在元类中
    // 3. 每个类只有一个类对象,每个类对象只有一个元类
    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;
        struct objc_method_list **methodLists;
        struct objc_cache *cache;
        struct objc_protocol_list *protocols;
    };
    
    f81c1ab1af0d8cee47e96db2568859d0.png

    前缀

    1. 苹果保留了两字符前缀,我们应该使用三字符前缀
    2. C函数名应该加上前缀
    3. 头文件中的全局变量需要加上前缀
    4. 为私有方法名添加前缀(例如p_),用于区分哪些方法是私有的,哪些是公有的,私有方法可以随意改动,公有方法则要三思而后行。
    5. 为第三方类添加分类时,分类中的方法要增加前缀,可以避免覆盖原有方法。

    使用段位缓存代理能否响应某个方法

    • 段位:结构体可以设置其成员所占的二进制位数
    struct {
        unsigned int fieldA : 8; // fieldA 占 8 个二进制位,即 0 ~ 255
        unsigned int fieldB : 4;
        unsigned int fieldC : 2;
        unsigned int fieldD : 1;}_delegateFlag;
    
    • 实例:
    @protocol KKSlideTabBarViewDelegate <NSObject>
    @optional;
    - (void)slideTabBarView:(KKSlideTabBarView *)tabBar pageChangedFromIndex:(NSUInteger)from toIndex:(NSUInteger)to;
    - (void)slideTabBarView:(KKSlideTabBarView *)tabBar itemMoreClicked:(UIButton *)itemMore;
    @end
    @interface KKSlideTabBarView : UIView
    @property (nonatomic,weak) id <KKSlideTabBarViewDelegate> delegate;
    @end
    
    @implementation KKSlideTabBarView {   
        struct {
            unsigned int didPageChangedHandle    : 1;
            unsigned int didItemMoreClickdHandle : 1;
        }_delegateFlags;
    }
    - (void)setDelegate:(id<KKSlideTabBarViewDelegate>)delegate {
        _delegate = delegate;
        _delegateFlags.didPageChangedHandle = [self.delegate respondsToSelector:@selector(slideTabBarView:pageChangedFromIndex:toIndex:)];
        _delegateFlags.didItemMoreClickdHandle = [self.delegate respondsToSelector:@selector(itemMoreClicked:)];
    
    }
    
    - (void)itemMoreClicked:(UIButton *)sender
    {
        if (_delegateFlags.didItemMoreClickdHandle) {
            [self.delegate slideTabBarView:self itemMoreClicked:sender];
        }}
    }
    

    NSOperation 与 GCD 优缺点

    • GCD 优点:纯 C api,更加轻量级。而operation 是对象,更加重量级
    • NSOperation 优点:
    1. 操作加入队列后可以取消(已经启动的任务无法取消)
    2. 可以自动操作之间的依赖关系
    3. 可以使用 KVO 监控 NSOperation 对象的属性,比如通过 isCancelled 判断任务是否取消, isFinished 属性判断任务是否完成
    4. 可以指定每个操作的优先级,而 GCD 只能指定队列的优先级
    5. 可以自定义 operation 对象

    使用 NSCache 和 NSPurgeableData 缓存数据

    • 只有费时操作才值得放入缓存,比如需要从网络获取的数据、从磁盘读取的数据
    @interface KKSlideTabBarViewController ()
    {
        NSCache *_cache;
    }
    @end
    
    _cache = [NSCache new];
    _cache.countLimit = 100;
    _cache.totalCostLimit = 5 * 1024 * 1024;
    
    NSPurgeableData *cacheData = [_cacheobjectForKey:@"url..."];
    if (cacheData) {
        // stop the data being purged
        [cacheData beginContentAccess];
       
        // 使用数据
        // ......
       
        // Mark that the data may be purged again
        [cacheData endContentAccess];
    } else {
        NSData *data = [NSData dataWithContentsOfURL:@"xxx"];
        NSPurgeableData *purgeableData = [NSPurgeableData dataWithData:data];
        [_cache setObject:purgeableData
                   forKey:@"url..."
                     cost:data.length];
       
        // With access already maked
       
        // user data
        // .....
       
        // Mark that the data may be purged now
        [purgeableData endContentAccess];
    }
    

    精简 + load 与 + initialize 方法

    它们都是在类载入系统时由运行时系统调用,不能手动调用。

    + load 方法:
    在类加载时调用。
    系统会先调用父类的 load 再调用子类的 load,先调用类本身 load,再调用 categery load。
    在 load 方法中,运行时系统处于脆弱状态,不能确定其他类是否加载完毕。不能在 load 方法中使用其他类对象,因为无法知道这个类是否加载了。
    + initialize 方法
    + initialize 方法是惰性加载,使用到类时才会调用。
    + initialize 方法调用时,系统处于正常状态,可以在 + initialize 方法中使用其他类对象。
    + initialize 方法一定会在线程安全的环境中执行,那么执行 + initialize 方法时会阻塞其他线程
    如果子类没有实现 + initialize 方法,而父类实现了,那么会调用夫类的。所以应该这么实现:

    + (void)initialize
    {
        if (self == [KKSlideTabBarView class]) {
            // 只有当 KKSlideTabBarView 类载入系统时才执行这里的代码
            // 不然的话 KKSlideTabBarView 的父类载入系统也会调用 initialize
        }
    }
    

    相关文章

      网友评论

          本文标题:《Effective Objective-C 2.0》读书笔记.

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