zoukankan      html  css  js  c++  java
  • Effective Objective-C 2.0 学习记录

      由于最近入职,公司安排自由学习,于是有时间将Effective Objective-C 2.0一书学习了一遍。由于个人知识面较窄,对于书中有些内容无法理解透彻,现将所学所理解内容做一遍梳理,将个人认为常用且重要的知识记录下来,以供日后参考。

      1.在类的头文件中尽量少引入其他头文件

      将头文件引入的时机尽量延后,在确有需要的时才引入(比如.m文件中)。因为头文件中引入其他类头文件,会增加编译时间(可能是现在运行硬件比较好,所以对此点没啥感觉)。在头文件中若要使用其他类,则用"向前声明"-->@class + 类名。

      2.多用类型常量,少用#define预处理指令

      因为用预处理指令定义出来的常量不含类型信息,编译器只会进行查找替换,即使有人重新定义了常量值,编译器也不会产生警告。建议使用方法如下

    //将#define
    #define ANIMATION_DURATION 0.3
    //用此句代码表示
    static const NSTimeInterval kAnimationDuration = 0.3;

    这样不仅可以知道常量类型 还可以将数据局限于本类文件中使用

      3.用枚举表示状态、选项、状态码

      初级编写代码人员可能直接写出枚举,编写其状态

    typedef enum PSPConnectionState{
        PSPConnectionStateDisconnected = 1,
        PSPConnectionStateConnecting,
        PSPConnectionStateConnected,
    }PSPConnectionState;

    初看好像并无不妥,但如果改变一下编写枚举的方式,写成如下所示

    //单选状态枚举
    typedef NS_ENUM(NSUInteger, PSPConnectionState){
        PSPConnectionStateDisconnected = 1,
        PSPConnectionStateConnecting,
        PSPConnectionStateConnected,
    };
    //复选状态枚举
    typedef NS_OPTIONS(NSUInteger, PSPConnectionState){
        PSPConnectionStateDisconnected = 1 << 0,
        PSPConnectionStateConnecting = 1 << 1,
        PSPConnectionStateConnected = 1 << 2,
    };

    这样用NS_ENUM和NS_OPTIONS宏来定义枚举类型,并指明其底层数据类型,除了可以确保枚举是开发者所选的底层数据类型实现出来外,还能够方便其他开发人员查看和使用。另外注意在处理枚举类型时可以尽量使用switch语句,并且不要实现default分支,这样的话,在新加如枚举之后,编译器就会提醒开发者switch语句没有处理所有的枚举。

      4.在对象内部尽量直接访问实例变量

      直接访问实例变量由于不经过Objective-C的"方法派发"步骤,所以访问速度会比较快,但由于直接访问实例变量不会触发"键值观测"(KVO),因此建议读数据时直接通过实例变量来读,而写入数据的时候,则通过属性来写(点语法)。当然对于惰性加载的属性,需要通过属性来读取数据。

      5.理解消息转发机制

      消息转发分为两大阶段:第一阶段先征询接受者所属类,看其是否能动态添加方法,以处理当前这个“未知的选择器”,这个叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”。消息整体转发流程通过下图来表示

    这里模拟在动态方法解析中添加方法 下面为代码示例

    =======.h文件中=========
    @interface BQAutoDictionary : NSObject
    
    //随意创建的属性
    @property (nonatomic, copy) NSString *string;
    @property (nonatomic, strong) NSNumber *number;
    @property (nonatomic, strong) NSDate *date;
    @property (nonatomic, strong) id opaqueObject;
    
    @end
    =======.m文件中=========
    @interface BQAutoDictionary()
    
    @property (nonatomic, strong) NSMutableDictionary *backingStore;
    
    @end
    
    @implementation BQAutoDictionary
    
    //不动态生成get,set方法 @dynamic
    string, number, date, opaqueObject; - (instancetype)init { self = [super init]; if (self) { _backingStore = [NSMutableDictionary new]; } return self; } + (BOOL)resolveInstanceMethod:(SEL)sel{ //获得无法处理消息名字 NSString *selectorString = NSStringFromSelector(sel); NSLog(@"%s",__func__); //动态添加set和get方法 if ([selectorString hasPrefix:@"set"]) { /** * 动态添加set方法 * @param self 类别 * @param sel 方法选择器 * @param IMP 需要增加的方法 */ class_addMethod(self, sel, (IMP)autodictionarySetter, "v@:@"); }else{ class_addMethod(self, sel, (IMP)autodictionaryGetter, "@@:"); } return YES; //若不能处理消息时需要返回下面方法,进行消息转发 //return [super resolveInstanceMethod:sel]; } id autodictionaryGetter(id self, SEL _cmd){ //得到实例中的字典 BQAutoDictionary *typedSelf = (BQAutoDictionary *)self; NSMutableDictionary *backingStore = typedSelf.backingStore; //根据选择器获取名字 NSString *key = NSStringFromSelector(_cmd); NSLog(@"Getter Name = %@",key); //返回值 return [backingStore objectForKey:key]; } void autodictionarySetter(id self, SEL _cmd, id value){ BQAutoDictionary *typedSelf = (BQAutoDictionary *)self; NSMutableDictionary *backingStore = typedSelf.backingStore; NSString *selectorString = NSStringFromSelector(_cmd); NSMutableString *key = [selectorString mutableCopy]; NSLog(@"Setter Name = %@",key); //移除‘:’字符 [key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)]; //移除'set'字符 [key deleteCharactersInRange:NSMakeRange(0, 3)]; //首字母改小写 NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString]; [key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar]; if (value) { [backingStore setObject:value forKey:key]; }else{ [backingStore removeObjectForKey:key]; } } @end

      6.用前缀避免命名空间冲突

      由于开发人员文件整合的时候经常出现命名重复的问题,但Objective-C没有命名空间机制。因此避免文件重命名的办法就是:为所有的名称都加上适当的前缀。Apple宣称其保留使用所有“两字母前缀”的权利,所以我们选用的前缀应该是三个字母的!

      7.尽量使用不可变对象

      属性是read-write,这样出来的类对象都是可变的。一般情况下我们要建模的数据未必都需要改变。比如网络服务的数据请求后,我们只是将数据进行展示。当然若希望某属性仅可于对象内部修改,则可在延展中将其属性由readonly扩展为readwrite。示例如下

    =====.h文件======
    @interface BQAutoDictionary : NSObject
    @property (nonatomic, readonly) NSString *name;
    @end
    =====.m文件======
    @interface BQAutoDictionary ()
    @property (nonatomic, readwrite, copy) NSString *name;
    @end

      8.在dealloc方法中只释放引用并解除监听

      对象在经历其生命周期后,最终会被系统回收,这时就要执行dealloc方法,因为此时对象已处于回收状态,因此不应在此方法内再做其他事情。只需要在里面释放指向其他对象的引用,并取消原来订阅的“键值观测”(KVO)或NSNotificationCenter等通知!注意对象所拥有的其他非Objective-C对象需要在这里手动释放,如果是手动管理内存,那么在最后还需要调用[super dealloc]

      9.多用派发队列,少用同步锁

      在以前的代码编写中,对于线程安全问题通常采用的做法是直接加线程锁。加线程锁的方法很好不过也有其缺陷,比如说,在极端情况下,同步块回导致死锁,另外其效率也不够高。这里就需要引入一个简单而高效的办法就是使用“串行同步队列”,用法如下

    _syncQueue = dispatch_queue_create("PSP", 0);
    
    - (NSString *)name{
        __block NSString *localName;
        dispatch_sync(_syncQueue, ^{
            localName = _name;
        });
        return localName;
    }
    - (void)setName:(NSString *)name{
        dispatch_sync(_syncQueue, ^{
            _name = name;
        });
    }

    当然还有一种更高效的方法用栅栏(barrier),栅栏块必须单独执行,不能与其它块并行(只对并发队列有意义)

    //并行队列
    _syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //用同步
    - (NSString *)name{
        __block NSString *localName;
        dispatch_sync(_syncQueue, ^{
            localName = _name;
        });
        return localName;
    }
    //利用异步栅栏块
    - (void)setName:(NSString *)name{
        dispatch_barrier_async(_syncQueue, ^{
            _name = name;
        });
    }

      10.构建缓存时选用NSCache而非NSDictionary

      在进行网络请求是如何缓存,大部分程序员可能是直接使用NSDictionary,其实NSCache类更好,它是Foundation框架专为处理这种任务而设计的NScache胜过NSDicitionary之处在于,当系统资源将要耗尽时,它可以自动删除缓存。如果采用普通的字典,那么就需要自己编写挂钩(我也不懂啥意思)。此外NSCache还会先行删除“最久未使用的”对象。另外还有个类叫做NSPurgeableData(NSMutableData子类),和NSCache搭配起来使用效果很好。具体使用方法可以自行百度!

      以上便是个人觉得需要整理的知识,若其中有什么错误之处,请大家指出,谢谢!

        

  • 相关阅读:
    window.location.href 失效的解决办法
    jquery.pagination.js分页
    64位WIN7+oracle11g+plsql安装
    uiZjs入门
    dateTimePicker的使用,时间控件
    jquery+ajax(用ajax.dll)实现无刷新分页
    用Jquery获取select的value和text值
    Asp.net上传文件Request.files获取不到文件
    手动导出Excel方法
    编写高质量代码改善C#程序的157个建议——建议121:为应用程序设定运行权限
  • 原文地址:https://www.cnblogs.com/purple-sweet-pottoes/p/4742552.html
Copyright © 2011-2022 走看看