《编写高质量iOS和OSX代码的52个有效方法》片段

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

static const NSTimeInterval kAnimationDuration = 0.3

使用类型常量定义常量含有类型信息,如果常量值类型不一致会有警告信息。

2.在头文件中使用extern声明全局常量,并在相关实现文件中定义其值。

EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;
EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;

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

1.如果选项表示为枚举类型,可以有多个选项可以同时使用,那么就将个选项值定义为2的幂,以便可以按位或操作将其组合。

typedef NS_ OPTIONS(NSUInteger,EOCPermittedDirection){
EOCPermittedDirectionUp = 1<<0,
EOCPermittedDirectionDown = 1<<1,
EOCPermittedDirectionLeft = 1<<2,
EOCPermittedDirectionRight = 1<<3,
};

2.用NS_ENUM,NS_OPTIONS宏来定义枚举类型,并指明起底层数据类型,可以保证枚举是用开发者选择的底层数据类型来实现,而不会采用编译器所选类型。

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

直接访问实例变量的原因:
*由于不经过Objective-C的“方法派发”步骤,直接访问实例变量速度比较快。
*直接访问实例变量时,不会调用“设置方法”绕过了相关属性的“内存管理语意”比如在ARC下,直接访问一copy属性,那么不会考呗该属性,只会保留新值释放旧值。
*如果直接访问实例变量,不会触发“键值观察KVO”通知。

1.在对象内部读取数据时,应该直接通过实例变量读取,写入数据时,通过属性来写。
2.在初始化方法及dealloc方法中,应该直接通过实例变量读写数据。
3.在懒加载中必须通过属性来读取数据。

5.以“类族模式”隐藏实现细节

这种模式最大的好处就是,可以隐藏抽象基类背后的复杂细节,使用者只需调用基类简单的方法就可以返回不同的子类实例。
如UIButton的类方法

+ (UIButton *)buttonWithType:(UIButtonType)type;

自定义个类族
首先定义抽象基类

typedef NS_ENUM(NSUInteger, EOCEmployeeType) {
EOCEmployeeTypeDeveloper, EOCEmployeeTypeDesigner, EOCEmployeeTypeFinance,
}
@interface EOCEmployee : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSUInteger salary;
@end

@implementation EOCEmployee
+ (EOCEmployee *)employeeWithType:(EOCEmployeeType)type {
      switch (type) {
          case EOCEmployeeTypeDeveloper:
               return [EOCEmployeeDeveloper new];
               break;
           case EOCEmployeeTypeDesigner:
                return [EOCEmployeeDesigner new];
                break;
           case EOCEmployeeTypeFinance:
                 return   [EOCEmployeeTypeFinance new];
                 break;
}

- (void)doADaysWork{
  
}

每个子类都是从基类继承而来。

@interface EOCEmployeeDeveloper : EOCEmployee
@end

@implementation EOCEmployeeDeveloper
- (void)doADaysWork{
    [super doADaysWork];
    NSLog(@"%@",[[self class] description]);
}
@end

6.消息转发机制

在“消息传递机制”中,当对象接收到无法解析的消息时,就会启动“消息转发”机制

1.动态方法解析

对象在收到无法解析的消息后,首先会调用所属类的

+ (BOOL)resolveInstanceMethod:(SEL)selector;

或者

+ (BOOL)resolveClassMethod:(SEL)selector;

在这里可以新增一个处理该"选择子"的方法,如

#import <Foundation/Foundation.h>

@interface EOCAutoDictionary : NSObject
@property (nonatomic, copy) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;
@end

#import "EOCAutoDictionary.h"
#import <objc/runtime.h>

@interface EOCAutoDictionary()
@property (nonatomic, strong) NSmutableDictionary *backingStore;
@end
@implementation EOCAutoDictionary
@dynamic string, number, date, opaqueObject;

- (id)init{
if (self = [super init]){
_backingStore = [NSMuatableDictionary new];
  }
 return self;
}

+ (BOOL)resolveInstanceMethod:(SEL)selector{
      NSString *selectorString = NSStringFromSelector(selector);
      if ([selectorString hsaPrefix:@"set"]){
          class_addMethod(self, selector,(IMP)autoDictionarySetter,"v@:@)"
      } else {
          class_addMethod(self, selector,(IMP)autoDictionaryGetter,"@@:");
      }
      return YES    
}

// Getter方法实现
id autoDictionaryGetter(id self, SEL _cmd){
    EOCAutoDictionary *typeSelf = (EOCAutoDictionary *)self;
    NSMutableDictionary *backingStore = typedSelf.backingStore;
    NSString *key = NSStringFromSelector(_cmd);
    return [backingStore objectForKey:key];

}
// Setter方法实现
id autoDictionarySetter(id self, SEL _cmd, id value) {
     EOCAutoDictionary *typeSelf = (EOCAutoDictionary *)self;
    NSMutableDictionary *backingStore = typedSelf.backingStore;
    NSString *selectorString = NSStringFromSelector(_cmd);
    NSMutableString *key = [selectorString mutableCopy];
// 删除结尾的:
    [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

// 使用
EOCAutoDictionary *dict = [EOCAutoDictionary new];
dict.date = [NSDate dateWithTimeIntervalSince1970:475328000];
NSLog(@"dict.date = %@",dict.date);
// 输出: dict.date = 1985-01-24 00:00:00 +0000

2.备援接收者

如果没有实现“动态方法方法解析”则会走这一步,能不能把消息转给其他接收者来处理,对应方法为

- (id)forwardingTargetForSelector:(SEL)selector

如果找到其他对象,则将其返回,没有找到,则返回nil。
注意:我们无法操作经由这一步的转发消息。

3.完整的消息转发

首先会创建NInvocation对象,把未处理的消息的全部细节都封装该对象中,包括选择子,目标,参数。在触发NSInvocation对象时,“消息派发系统”会吧消息指派给目标对象,此步骤会调用下列方法来转发消息:

- (void)forwardInvocation:(NSInvocation *)invocation;

如果还是没有处理该消息,则会调用“doesNotRecognizeSelector:”抛出异常

7.使用自动释放池降低内存峰值

NSArray *databaseRecords = [NSArray Array];
NSMutableArray *people = [NSMutableArray New];
for (NSDictionary *record in databaseRecords) {
     @autoreleasepool {
        EOCPerson *person = [EOCPerson alloc] initWithRecord:record];
        [people addObject:person];
     }

}

8.栈块,堆块,全局块

// 由于定义在if和else中的两个块都分配在占栈内存中,离开相应的范围可能会被释放,
void (^block)();
if (some condition) {
   block = ^ {
          NSLog(@"Block A");
   };
} else {
    block =^ {
         NSLog(@"Block B");
    };
}
block();

// 可以给块对象发送Copy消息拷贝到堆中,防止过早释放
void (^block)();
if (some condition) {
   block =[ ^ {
          NSLog(@"Block A");
   } copy];
} else {
    block =[^ {
         NSLog(@"Block B");
    } copy];
}
block();

// 全局块声明在全局内存中,这种快不会捕捉任何状态(比如外围变量等),全局快的拷贝操作是个空操作
void (^block)() =^{
    NSLog(@"This is a block");
};

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

如果多线程同时执行同一份代码时,我们需要使用锁来实现同步访问,
第一种方法,同步块

- (void)synchronizedMethod{
       @synchronized(self) {
          // 安全代码
        }
}
// 滥用@s'ynchronized(self)则会降低代码效率,因为所有同步块都会彼此抢夺同一个锁,要是有多个属性都这么写的话,每个属性的同步块都要等其他所有同步块执行完毕才能执行,

第二种方法, 使用NSLock

_lock = [[NSLock alloc] init];
- (void)synchronizedmethod{
     [_lock lock];
    // 安全代码
    [_lock unlock];
}

第三种方法,递归锁(NSRecursiveLock)

NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    KYS_GLOBAL_QUEUE(^{
        static void (^RecursiveBlock)(int);
        RecursiveBlock = ^(int value) {
            [lock lock];
            if (value > 0) {
                NSLog(@"加锁层数 %d", value);
                sleep(1);
                RecursiveBlock(--value);
            }
            [lock unlock];
        };
        RecursiveBlock(3);
    });

第二种和第三种也有缺陷,在极端情况下,同步快会导致死锁,效率也不是很高

第四种方法, 使用串行同步队列
把读取和写入操作都安排在同一个队列里,保证数据同步

_syncQueue = dispatch_queue_create("com.effectiveobjective.syncQueue".NULL);
- (NSString *)someString{
    _block NSString *localSomeString;
    dispatch_sync(_syncQueue, ^{
          localSomeString = _someString;
    });
    return localSomeString;
}

- (void)setSomeString:(NSString *)someString {
    dispatch_sync(_syncQueue, ^{
      _someString = someString;
    });
}

第五中,使用并发队列加栅栏,性能更佳

_syncQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- (NSString *)someString{
   _block NSString *localSomeString;
   dispatch_sync(_syncQueue, ^{
         localSomeString = _someString;
   });
   return localSomeStrng;
}

- (void)setSomeString:(NSString *)someString{
     dispatch_barrier_async(_syncQueue, ^{
       _someString = someString;
     });
}

10.打破NSTimer的循环引用

// 创建NSTimer分类,使用弱引用打破循环

 #import <Foundation/Foundation.h>
@interface NSTimer (EOCBlocksSupport)
+ (NSTimer *)etc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats;
@end

@implementation NSTimer (EOCBlocksSupport)
+ (NSTimer *)etc_scheduledTimerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats{
    
   return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(etc_blockinvoke:) userInfo:[block copy] repeats:repeats];
}

+ (void)eoc_blockInvoke:(NSTimer *)timer {
   void (^block)() = timer.userInfo;
    if (block) {
     block();
    }
}

// 使用

- (void)startPolling {
    __weak EOCClass *weakSelf = self;
    _pollTimer = [NSTimer eoc_scheduledTimerWithTimeInterval:5.0 block:^{ 
    EOCClass *strongSelf = weakSelf;
    [strongSelf p_doPoll];
    } 
    repeats:YES];

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容