程序员

编写高质量iOS与OSX代码的52个有效方法-第三章:接口与AP

2018-07-18  本文已影响15人  竹与豆

15、使用前缀避免命名空间冲突

1、重命名符号错误

OC没有其他语言内置的命名空间(namespace),命名时要避免潜在的命名冲冲突(naming clash):


15-1.png

比如如下错误,就是重命名符号错误(duplicate symbol error)。

duplicate symbol _OBJC_CLASS_$_DogObject in:
    xxx/DogObject-ED8631F460AAA56A.o
    xxx/DogObject-917EE703FAC7406E.o
duplicate symbol _OBJC_METACLASS_$_DogObject in:
    xxx/x86_64/DogObject-ED8631F460AAA56A.o
    xxx/DogObject-917EE703FAC7406E.o
ld: 2 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

另,苹果宣称保留使用所有两个字母的前缀(two-letter Prefix)的权利。所以自己选用前缀最好是三个字母。

2、给新增分类和分类方法加上前缀(第25条)

分类机制通常用于向无源码的既有类中新增功能。

分类的方法是直接加到类中的,就好比是类中固有的方法,将分类方法加入类中这一操作时在运行期系统加载分类时完成的。运行期系统会把分类中所实现的每个方法都加入类的方法列表中。

如果类中本来就有这个方法,分类中又实现了一次,那么分类中方法会覆盖原来的实现代码。有可能会发生多次覆盖。

如果多个分类名称相同,在运行期,是不会报错的。但是加载的分类有可能不是你所期望的。

如果有相同的方法,那么运行时调用的不一定是你想要的方法。

运行期不会报错,但是在实现结果的时候,就会出现未知错误。

15-2.png

比如实现了NSString的两个分类,同时都有一个分类方法"- (NSString *)urlEncordedString;"那么在调用过程中,就不知道是调用哪个分类中实现的方法。同样能够正常编译通过。

NSString *urlString = @"http://www.baidu.com";
NSLog(@"%@",[urlString urlEncordedString]);

因为不会报错,这种问题比较难发现,所以在写之前就避免这种情况就显得非常重要。

当然如果分类名相同,但是方法名不同时,有可能出现的问题是:No visible @interface for 'NSString' declares the selector 'seconString',你无法调用自己实现的方法。系统只是提供最后加载到的分类,如此而已。

3、类的实现文件中所用的纯C函数及全局变量

在类的实现文件中所用的纯C函数及全局变量,在编译好的目标文件中,这些名称要算作“顶级符号”(top-level symbol)。

如果在不同的类文件中实现同样的C函数,就会报重命名符号错误(duplicate symbol error)

15-3.png
duplicate symbol _completion in:
    xxx/ViewController.o
    xxx/NSString+Http.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

duplicate symbol _completion会指出错误方法名completion,另外会在下面的描述中指明在那些文件中出现冲突。

同样的,即使在实现文件中声明全局静态变量,在不同文件中声明相同名称的变量,也会出现名称冲突错误:

15-4.png
duplicate symbol _NameString in:
    xxx/ViewController.o
    xxx/NSString+Http.o
ld: 1 duplicate symbol for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

4、所开发的程序库中用到第三方库,给第三方库加前缀

这个问题很简单,如果要把自己封装的程序库给别人用,同时使用了不同的第三方库。那么在别人引用的时候,如果他工程中也使用相同的第三方库,就会出现重命名符号错误。

另外考虑到所使用第三方库版本不同,那么,在封装自己的程序库时就要将所用到的第三方库中的文件添加前缀,避免此类问题。

16、提供全能初始化方法

全能初始化方法(designated initializer):为对象提供必要信息以便其能完成工作的初始化方法。

可以通过警告或者设置默认值调用全能初始化方法的方式,实现初始化。

全能初始化方法的调用链一定要维系,也即是,集成关系中,初始化方法的维护调用。

Mac OS X 的APPKit会iOS的UIKit两个UI框架都广泛运用序列化机制(serialization mechanism),将对象序列化,保存至XML格式的XIB文件中农。这些XIB文件通常用来存放视图控制器机器视图布局。加载NIB文件时,系统会在解压缩的过程中解码视图控制器。

17、实现description方法

实现description方法返回一个有意义的字符串,用以描述该实例。

- (NSString *)description {
    return [NSString stringWithFormat:@"%@ %zd",_dogName,_dogAge];
}

若想在调试时(LLDB)打印出更详尽的对象描述信息,则应实现debugDescription方法。

- (NSString *)debugDescription {
    return [NSString stringWithFormat:@"<%@: %p \"%@ %zd \">",[self class],self,_dogName,_dogAge];
}

打印结果

17-1.png

18、尽量使用不可变对象 --

关联第6条-属性

尽量减少对象中的可变内容,应该尽量把对外公布出来的属性设为只读,并且只在必要时才将属性对外公布。

如果想要修改封装在对象内部的数据,同时不将哲学数据为外人所动,可以在对象内部将readonly属性重新声明为readwrite。

在定义类的公共API时,对象里表示各种collection的那些属性究竟应该设成可变的,还是不可变的。

19、使用清晰协调的命名方式

OC中一般采用驼峰式大小写命名法。

1)方法命名

- (NSString *)stringOfDogInfomation;

- (NSDictionary *)dictionaryOfDogInfomation;
[string lowercaseString];

[string hasSuffix:@"this"];
[string isEqualToString:@"xxxx"];

清晰明了,统一规范

2)类与协议的命名

应该为类和协议加上前缀,避免命名空间冲突。

命名方式要协调一致,如果要从其他框架中继承子类,务必遵循其命名惯例。

若要自定义委托协议,则名称中应包含委托发起方的名字,再加上Delegate

20、为私有方法名加前缀

为在内部使用的私有方法加前缀,区分公共方法和私有方法,便于修改方法名和方法签名。

依据个人习惯,p_method

21、理解OC错误模型

1)异常 exception --fatal error致命错误

OC中,在激起罕见的情况下抛出异常,异常抛出之后不再考虑恢复问题,应用程序此时应该退出。不需要再辨析复杂的“异常安全”代码。

异常一般只用于处理严重错误(fatal error 致命错误)。

@throw [NSException exceptionWithName:NSInternalInconsistencyException reason:string userInfo:nil];

比如编写某个抽象基类,正确用法是先从中集成一个子类,再用这个子类。这种情况下,如果直接使用了这个抽象基类的,那么可以抛出异常。

OC没有办法将某个类标记为“抽象类”。要想达成效果,最好的办法是在那些子类必须覆写的超类方法里抛出异常。

2)其他错误 --nonfatal error 非致命错误

OC语言所用编程范式为:令方法返回nil/0,或是使用NSError,表明有错误发生。

NSError对象封装了三条消息:

NSError的用法:

- (BOOL)doSomething:(NSString *)thing error:(NSError **)error {
    if ([thing isEqualToString:@"1"]) {
        return YES;
    }
    *error = [NSError errorWithDomain:NSURLErrorDomain code:100 userInfo:@{@"key":@"something wrong"}];
    return NO ;
}
NSError *error;
BOOL ret  = [littleDog doSomething:@"0" error:&error];
if (!ret) {
    NSLog(@"error : %@",[error debugDescription]);
}

另外,定义自己的指定的专用错误范围字符串,使用这个字符串创建NSError对象,就能确定错误来源。

extern NSString *const ZYDErrorDomain;

typedef NS_ENUM(NSUInteger,ZYDError) {
    ZYDErrorUnknown                 = -1, //未知错误
    ZYDErrorBadInput                = 500,
};

3)

22、理解NSCopying协议

1)不可变拷贝 NSCopying

OC 中如果需要拷贝对象,需要通过copy方法完成。如果希望自己的类支持拷贝操作,就要实现NSCopying协议。该协议只要一个方法:

- (id)copyWithZone:(NSZone *)zone;

在以前开发程序时,会据此吧内存分成不同的区(zone),而对象会创建在某个区里面。现在不用,每个程序只有一个区:默认区(default zone)。所以需要实现这个方法,但是不用担心zone参数。

若要某个类支持拷贝功能,需要改类声明遵从NSCoping协议,并实现其中的方法就可以。

.h
@interface DogObject : NSObject <NSCopying>

@end
.m

@interface DogObject()
{
    NSMutableArray *_familys;//内部成员变量,并非属性
}
@end

@implementation DogObject

- (instancetype)initWithDogName:(NSString *)dogName age:(NSInteger)age {
    self = [super init];
    
    if (self) {
        _dogName = [dogName copy];
        _dogAge = age;
    }
    return self;
}

#pragma mark -- NSCopying
- (id)copyWithZone:(NSZone *)zone {
    DogObject *copy = [[[self class] allocWithZone:zone] initWithDogName:_dogName age:_dogAge];
    copy -> _familys = [_familys mutableCopy]; //有
    return copy;
}

2)可变拷贝 NSMutableCopying

定义一个方法:
- (id)mutableCopyWithZone:(NSZone *)zone,与copy类似,也用默认的zone参数来调mutableCopyWithZone:。如果类分为可变版本,可不可变版本,需要实现NSMutableCopying。

3)深拷贝

深拷贝:在拷贝对象自身是,将其底层数据也一并复制过去。

浅拷贝:之拷贝容器对象本身,而不复制漆黑中的数据。

容易内的对象并不都能拷贝,而且调用者也未必要在拷贝容器的同时一并拷贝其中的每个对象。

一般NSCopying大多数情况下执行的是浅拷贝,如需要在对象上执行深拷贝,那么除非该类的文档说它是用深拷贝来实现NSCopying协议的,否则,要么寻找能够执行深拷贝的方法,要么自己编写方法来实现。

比如,NSArray中的方法:- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag如果flag为YES,该方法会向数组中每个元素发送copy信息,用拷贝好的创建新的Array,并返回给调用者。

可以给对象创建自定义深拷贝方法:

- (id)deepCopy {
    DogObject *copy = [[[self class] alloc] initWithDogName:_dogName age:_dogAge];
    copy -> _familys = [[NSMutableArray alloc] initWithArray:_familys copyItems:YES];
    return copy;
}

4)

上一篇下一篇

猜你喜欢

热点阅读