美文网首页移动开发俱乐部
Effective Objective-C 52方法要点笔记

Effective Objective-C 52方法要点笔记

作者: 恩莱客 | 来源:发表于2021-03-22 16:11 被阅读0次

    第一章:熟悉Objective-C

    1. 特性:动态语言,运行时确定绑定关系
    2. 减少头文件的引用;记住一个“向前声明”;避免类相互引用;使用#import
    3. 多用字面量写法(现代语法),少用等价长方法
    4. 多用类型变量,少用#define预处理指令,如:
    • 类内使用static const NSInteger kTime = 0.3
    • 类外使用,名称使用类名为前缀
    xxx.h
    extern nsstring *const classname_a;
    xxx.m
    nsstring const classname_a = @"value";
    
    5. 用枚举表示状态、选项、状态码,配合Switch使用。
    typedef NS_ENUM(NSInteger, UIButtonRole) {
        UIButtonRoleNormal,
        UIButtonRolePrimary,
        UIButtonRoleCancel,
        UIButtonRoleDestructive
    } API_AVAILABLE(ios(14.0));
    
    typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
        SDRectCornerTopLeft     = 1 << 0,
        SDRectCornerTopRight    = 1 << 1,
        SDRectCornerBottomLeft  = 1 << 2,
        SDRectCornerBottomRight = 1 << 3,
        SDRectCornerAllCorners  = ~0UL // 无符号长整型0
    };
    

    第二章:对象、消息、运行期

    OC编程 = 对象(基本结构单元)+ 消息传递(messaging)

    6. 属性
    • 封装对象数据
    • 点语法 - 访问对象数据,编译器在编译时,生成存取方法和变量:
      Property = _str + - (NSString *)str + - (void)getStr:(NSString *)str
    • 属性特质(修饰符),控制编译器生成存取方法
    7.在对象内部尽量直接访问实例变量
    • 读数据 - 直接使用实例变量
    • 写数据 - 使用属性
    • 初始化方法或者dealloc方法中直接使用实例变量来读、写数据
    • 懒加载时,使用属性来读取数据
    8. 对象等同性
    • 对比:
      == 指针相等
      hash值(先判断) + (BOOL)isEqual:(id)object;
      重写isEqual方法:
    - (BOOL)isEqual:(id)object {
        if (self == object) {//指针相等
            return YES;
        }
        if (![object isKindOfClass:[Person class]]) {//同类
            return NO;
        }
        return [self isEqualToPerson:(Person *)object];
    }
    - (BOOL)isEqualToPerson:(Person *)person {//类中数据相等(即属性)
        if (!person) {
            return NO;
        }
        BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
        BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
        return haveEqualNames && haveEqualBirthdays;
    }
    

    重写hash方法

    //In reality, a simple XOR over the hash values of critical properties is sufficient 99% of the time(对关键属性的hash值进行位或运算作为hash值)
    - (NSUInteger)hash {
        return [self.name hash] ^ [self.birthday hash];
    }
    
    • 特定类等同判定方法
    - (BOOL)isEqualToDictionary:(NSDictionary<KeyType, ObjectType> *)otherDictionary;
    - (BOOL)isEqualToArray:(NSArray<ObjectType> *)otherArray
    - (BOOL)isEqualToString:(NSString *)aString;
    
    9. 以“类簇”模式隐藏实现细节

    原理解释:通过一个对象(类)来存储不同类型的数据变量,其内部不能修改。

    NSNumber
    注:请不要尝试去创建NSString、NSArray或NSDictionary的子类。如果必须添加或修改某个方法,可以使用类别的方式。
    10. 关联对象(AssociatedObject)- 把两个对象关联起来
    • 分类 - 扩展方法
    • 关联对象 - 扩展属性
    // 关联对象:policy 内存管理策略
    objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
                             id _Nullable value, objc_AssociationPolicy policy)
    // 获取关联对象
    objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
    // 删除关联对象
    objc_removeAssociatedObjects(id _Nonnull object)
    
    11. 消息传递
    // self - receiver接收者、op - Selector选择子
    objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
    

    发送消息 = 调用方法

    //
    id returnValue = [someObject messageName:parameter];
    id returnValue = objc_msgSend(someObject, @selector(messageName:), paramater);
    
    12. 消息转发机制

    例:[obj test]; - > objc_msgSend(obj, test)(runtime方法)
    runtime的执行步骤:

    1. obj(isa) - > class
    2. 在class method list中找到test
    3. 没有找到test,找superclass
    4. 找到test后,执行IMP
    动态方法解析 + 消息转发

    相关API:

    + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    - (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    - (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
    

    代码实现过程:

    1. 定义Property,声明为@dynamic,没有实现方法
    2. 在方法
    + (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
    

    添加转发代码。

    13. 方法调配技术 - 运行时,交换类方法

    涉及方法:

    OBJC_EXPORT IMP _Nonnull
    method_setImplementation(Method _Nonnull m, IMP _Nonnull imp) ;
    OBJC_EXPORT void
    method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2) );
    

    以下是腾讯云SDK的捕获方法调用异常代码。

    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            [self changeImplementation];
        });
    }
    + (void)changeImplementation {
        Class class = object_getClass((id)self);
        Method originMethod = class_getClassMethod(class, @selector(exceptionWithName:reason:userInfo:));
        Method replacedMethod = class_getClassMethod(class, @selector(qcloud_exceptionWithName:reason:userInfo:));
        method_exchangeImplementations(originMethod, replacedMethod);
    }
    +(NSException *)qcloud_exceptionWithName:(NSExceptionName)name reason:(NSString *)reason userInfo:(NSDictionary *)userInfo{
        NSException *exp = [self qcloud_exceptionWithName:name reason:reason userInfo:userInfo];
        [QualityDataUploader trackSDKExceptionWithException:exp];
        return exp;
    }
    
    14. 理解类对象的用意

    SomeClass的子类从NSObject中继承而来,其继承体系:


    SomeClass实例所属的“类继承体系”

    每个类仅有一个“类对象”,每个“类对象”仅有一个与之相关的“元类”。

    • 定义id对象
    typedef struct objc_class *Class;
    /// Represents an instance of a class.
    struct objc_object {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    };
    /// A pointer to an instance of a class.
    typedef struct objc_object *id;
    
    • 此结构体存放类的“元数据metadata”:
      isa - 对象所属类型
      super_class - 元类
    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    
    #if !__OBJC2__
        Class _Nullable super_class                              OBJC2_UNAVAILABLE;
        const char * _Nonnull name                               OBJC2_UNAVAILABLE;
        long version                                             OBJC2_UNAVAILABLE;
        long info                                                OBJC2_UNAVAILABLE;
        long instance_size                                       OBJC2_UNAVAILABLE;
        struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
        struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
        struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
        struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    #endif
    
    } OBJC2_UNAVAILABLE;
    
    • 类型查询体系
    // 类或者派生类的实例
    - (BOOL)isKindOfClass:(Class)aClass;
    // 特定类的
    - (BOOL)isMemberOfClass:(Class)aClass;
    
    16. 提供“全能初始化方法”
    Person.h
    @interface Person : NSObject
    @property (nonatomic, assign) float age;
    @property (nonatomic, strong) NSString *name;
    // 全能初始化方法
    - (id)initWithAge:(CGFloat)age
             withName:(NSString *)name;
    @end
    
    Person.m
    @implementation Person
    - (id)initWithAge:(CGFloat)age
             withName:(NSString *)name{
        if (self = [super init]) {
            _age = age;
            _name = name;
        }
        return self;
    }
    // 覆写超类的init方法
    - (instancetype)init{
        return [self initWithAge:0 withName:@""];
       // @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"replace init with new function" userInfo:nil];
    
    }
    @end
    
    
    17. 自定义对象的description,po调试使用,或者对象类型强转打印
    - (NSString *)description{
        return [NSString stringWithFormat:@"%@ is %ld years old", _name, (long)_age];
    }
    

    断点debug打印:

    (lldb) po person
    Lucy is 20 years old
    

    对象类型强转后打印:

    (lldb) po person
    <Person: 0x600003e79960>
    
    (lldb) po ((Person *)person).name
    Lucy
    
    (lldb) po ((Person *)person).age
    20
    
    18. 尽量使用不可变对象

    尽量创建不可变对象,对象内部确实需要修改时,才把扩展属性有readonly换成readwrite(或缺省),外部使用时,使用方法公开、修改回调。

    19. function命名方式
    // 返回类型 with 传参1...2...3 
    + (instancetype)stringWithString:(NSString *)string;
    返回类型 with 传参1...2...3  属性赋值
    + (instancetype)stringWithCharacters:(const unichar *)characters length:(NSUInteger)length;
    // “是否有”
    - (BOOL)hasPrefix:(NSString *)str;
    // "是否相等"
    - (BOOL)isEqualToString:(NSString *)aString;
    
    20. 区分公共方法和私有方法

    公共方法名尽量不要修改,修改后外部调用会出错。
    给私有方法前边加上p_,如:

    - (void)p_privateMethod;
    

    或者

    #pragma mark  public
    // 外部访问方法
    
    #pragma mark  private
    // 内部方法
    
    21. “异常(NSException)”处理机制

    极端情况下抛出异常,不用考虑恢复,所以异常代码简单就行。

    - (QCloudSignature *)signatureForData:(id)signData {
        @throw [NSException exceptionWithName:QCloudErrorDomain reason:@"请在子类中实现该函数" userInfo:nil];
    }
    
    22. 理解NSCopying、NSMutableCopying协议
    @protocol NSCopying
    
    - (id)copyWithZone:(nullable NSZone *)zone;
    
    @end
    
    @protocol NSMutableCopying
    
    - (id)mutableCopyWithZone:(nullable NSZone *)zone;
    
    @end
    
    

    类遵循了NSCopying协议,其对象就支持copy操作。
    具体代码实现:

    - (nonnull id)copyWithZone:(nullable NSZone *)zone {
        return [self doCopyWithZone:zone];
    }
    
    - (id)doCopyWithZone:(nullable NSZone *)zone {
        TVideoSegmentItem *model = [[TVideoSegmentItem allocWithZone:zone] init];
        [[[self class] getPropertyNameList:[model class]] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            id value = [self valueForKey:obj];
            if ([value isKindOfClass:[NSMutableDictionary class]] ||
                [value isKindOfClass:[NSDictionary class]]) {
                NSMutableDictionary *newObj = [[NSMutableDictionary alloc] initWithDictionary:value copyItems:YES];
                [model setValue:newObj forKey:obj];
            }
            else if ([value isKindOfClass:[NSArray class]] ||
                     [value isKindOfClass:[NSMutableArray class]]) {
                NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:value copyItems:YES];
                [model setValue:newArray forKey:obj];
            }
            else if ([value isKindOfClass:[NSMutableString class]] ||
                     [value isKindOfClass:[NSString class]]) {
                NSMutableString* newObj = [value mutableCopy];
                [model setValue:newObj forKey:obj];
            }
            else {
                [model setValue:[self valueForKey:obj] forKey:obj];
            }
        }];
        return model;
    }
    
    + (NSArray *)getPropertyNameList:(Class)cls {
        NSMutableArray *propertyNameListArray = [NSMutableArray array];
        unsigned int count = 0;
        objc_property_t *properties = class_copyPropertyList(cls, &count);
        for (NSInteger i = 0 ; i < count; i++) {
            const char *propertyCharName = property_getName(properties[i]);//c的字符串
            NSString *propertyOCName = [NSString stringWithFormat:@"%s",propertyCharName];//转化成oc 字符串
            [propertyNameListArray addObject:propertyOCName];
        }
        NSArray *dataArray = [NSArray arrayWithArray:propertyNameListArray];
        return dataArray;
    }
    

    注:

    // NSCopying
    [NSMuatbleArray copy] => array
    // NSMutableCopying
    [NSArray mutableCopy] => mArray
    
    深拷贝与浅拷贝对比图
    浅拷贝后内容与原内容均指向相同对象,而深拷贝后的内容是原内容对象的拷贝的一份。注意其内容对象的可变性未变。

    相关文章

      网友评论

        本文标题:Effective Objective-C 52方法要点笔记

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