美文网首页
iOS - OC <<编写高质量iOS与OS X代码的多个有效方

iOS - OC <<编写高质量iOS与OS X代码的多个有效方

作者: 洧中苇_4187 | 来源:发表于2020-09-17 17:26 被阅读0次

    7.以"类簇模式"隐藏实现细节

    iOS中数组有多少种

        NSArray *array0 = @[@1,@2,@3];
        NSArray *array1 = [[NSArray alloc]init];
        NSArray *array2 = [[NSArray alloc]initWithObjects:@1, nil];
        NSMutableArray *array3 = array0.mutableCopy;
        NSArray *array4 = [NSArray alloc];
        NSLog(@"\n%@\n%@\n%@\n%@\n%@",array0.class,array1.class,array2.class,array3.class,array4.class);
    
    //打印结果------------------------------
    2020-09-16 18:36:22.140681+0800 testProj01[58466:3176044] 
    __NSArrayI
    __NSArray0
    __NSSingleObjectArrayI
    __NSArrayM
    __NSPlaceholderArray
    

    1.创建一个对象的时候,类型并不像我们表面看到的那样,像数组这种类簇比较多的类;
    2.子类尽量复写超类中指明需要复写的方法.;
    3.从类簇的公共抽象积累中继承子类时要当心,若有开发文档,则应首先阅读.

    8.在既有类中使用关联对象存放自定义数据

    //该方法以给定的键和策略为某对象设置关联对象值
    objc_setHook_getImageName(objc_hook_getImageName  _Nonnull newValue, objc_hook_getImageName  _Nullable * _Nonnull outOldValue)
    //该方法以给定的键从某对选哪个中获取相应的关联对象值
    objc_getAssociatedObject(id  _Nonnull object, const void * _Nonnull key)
    //该方法移除指定对象的全部关联对象
    objc_removeAssociatedObjects(<#id  _Nonnull object#>)
    

    关联对象的键一般采取全局静态变量的地址作为键,这里建议采用如下这种写法,
    static const char MJReplacedKeyFromPropertyNameKey = '\0';

    使用如下

    这点我在阅读 MJExtension框架 的时候,第五点有提及.(注意:objc_getAssociatedObject设置的object 不能为nil,否则会报BAD_ACCESS的错;存取的值必须是对象类型).

    9.理解objc_msgSend的作用

    objective-c 中方法的调用直到运行期间才能确定,这个过程叫动态绑定(dynamic binding),方法的调用就是在类的方法列表(method_list)中寻找方法,当然,在这之前会先去方法缓存中去寻找,大致流程就是,personClass_cache-->personClass_method_list--->personClass_super_cache-->personClass_super_method_list ... -->message forwarding(消息转发),即从当前类的缓存中寻找方法,没有就从方法列表寻找,还没有,从父类缓存寻找,父类方法别表中寻找,直到找到元类还没有(即消息无法解读),就执行消息转发流程.

    1.objc_msgSend还有一些边界情况, objc_msgSend_stret当消息的返回为结构体时,通过这个来处理;
    2.objc_msgSend_fpret如果消息的返回为浮点数,这个函数来处理;
    3.如果要交给父类,则objc_msgSendSuper,调用到父类的情况.

    如果函数最后一项操作是调用另外一个函数,那可以调用"尾调用优化",编译器会生成调转至另一函数所需的指令码,而不会向调用堆栈推入新的栈针(frame stack)

    消息转发流程图

    10.理解"类对象"的用意

    typedef struct objc_object {
          Class isa;
    } *id;
    

    isa 其实 就是 is a, 如果这个对象继承自NSString,则Class is a NSString,读起来是不是更像句子,所以isa其实是告诉你,对象的类型信息
    类对象本身所具有的元数据,也是类对象所属的类型,叫做元类

    当我们使用如下这些方法的时候,其实是调用 super_class 指针来确定的,
    
    [dict isMemberOfClass:EOCAutoDictionary.class];
    [dict isKindOfClass:EOCAutoDictionary.class];
    
    不建议采取classA.class == ClassB.class,来判断两个类是否相等,
    因为可能出现某个类继承自NSProxy的情况,这样就不会打印
        MyProxy *tmpProx = MyProxy.alloc;
        if (tmpProx.class == NSProxy.class) {
            NSLog(@"OCAutoDictionary.class");
        }
    

    尽量使用类型信息查询方法(isKindOfClass,isMenberOfClass)来确定对象类型,不要直接比较类对象,因为某些对象可能实现了消息转发功能.

    11.提供全能初始化方法

    以 NSDate 为例
    - (instancetype)init;
    - (instancetype)initWithTimeIntervalSinceReferenceDate:(NSTimeInterval)ti;
    - (nullable instancetype)initWithCoder:(NSCoder *)coder
    - (instancetype)initWithTimeIntervalSinceNow:(NSTimeInterval)secs;
    - (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)secs;
    - (instancetype)initWithTimeInterval:(NSTimeInterval)secsToBeAdded sinceDate:(NSDate *)date;
    

    在上面几个初始化方法中 initWithTimeIntervalSinceReferenceDate,是全能初始化方法,也就是说其他的方法都调用该初始化方法,当底层存储数据改变时,无需改动其他初始化方法,其实很多第三方开源框架中都采取了这种设计模式.

    1.在类中提供一个全能初始化方法,并于文档里指明,其他初始化方法都应该调用此方法.
    2.若全能初始化方法与父类不同,则需要覆盖父类中对应方法.
    3.如果父类的初始化方法不适用于子类,那么应该覆盖这个父类方法,并抛出异常.

    12.实现description方法

    //In header file
    @interface EOCPerson : NSObject
    - (instancetype)initWithFirstName:(NSString *)firstName
                             lastName:(NSString *)lastName;
    @end
    
    //In implementation file
    
    #import "EOCPerson.h"
    @interface EOCPerson()
    @property(nonatomic,strong)NSString *firstName;
    @property(nonatomic,strong)NSString *lastName;
    
    @end
    @implementation EOCPerson
    - (instancetype)initWithFirstName:(NSString *)firstName
                             lastName:(NSString *)lastName
    {
        self = [super init];
        if (self) {
            _firstName = firstName;
            _lastName = lastName;
        }
        return self;
    }
    - (NSString *)description
    {
        return [NSString stringWithFormat:@"%@ ---  %@",_firstName,_lastName];
    }
    
    - (NSString *)debugDescription
    {
        return [NSString stringWithFormat:@"<%@: %p \"%@ %@\">",self.class,self,_firstName,_lastName];
    }
    @end
    
    
    //使用
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        EOCPerson * person = [EOCPerson.alloc initWithFirstName:@"Steve" lastName:@"Bob"];
        
        NSLog(@"%@",person);
    }
    

    description方法就是我们在NSLog打印这个对象的时候出会调用,而debugDescription方法是我们在调试阶段控制台lldb的时候,我们po person才会打印出来,这样有利于我们调试程序,发现线上bug等等.

    相关文章

      网友评论

          本文标题:iOS - OC <<编写高质量iOS与OS X代码的多个有效方

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