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

[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
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容