iOS tips 5

2015-12-01 | 阅读

OC枚举 NS_ENUM,NS_OPTIONS

一般使用C风格的枚举,会如此声明:

typedef enum {
    SKeyboardTypeCharacters= 0,
    SKeyBoardTypeNumber ,
    SKeyboardTypeSymbol ,
}LLHKeyboardType;

OC中使用NS_ENUM来表示普通枚举,而NS_OPTIONS表示位移枚举,有:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {  
    UIViewAnimationTransitionNone,//默认从0开始  
    UIViewAnimationTransitionFlipFromLeft,  
    UIViewAnimationTransitionFlipFromRight,  
};  
  
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {   
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,  
    UIViewAutoresizingFlexibleWidth        = 1 << 1,  
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2, 
};  

而这两个宏的定义为:

#if (__cplusplus && __cplusplus >= 201103L && (__has_extension(cxx_strong_enums) || __has_feature(objc_fixed_enum))) || (!__cplusplus && __has_feature(objc_fixed_enum))  
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type  
#if (__cplusplus)  
#define NS_OPTIONS(_type, _name) _type _name; enum : _type  
#else  
#define NS_OPTIONS(_type, _name) enum _name : _type _name; enum _name : _type  
#endif  
#else  
#define NS_ENUM(_type, _name) _type _name; enum  
#define NS_OPTIONS(_type, _name) _type _name; enum  
#endif  

OC中用Category来实现private和default

OC语法是动态的,没有真正的private,一般将函数不在头文件中说明,或者在私有的category的头文件中声明,这就是OC中的private.

category的活用还是很重要的,在java中,有一种作用域default,也就是不写作用域时候的状态,对包内对象可见,而对包外对象以及派生类不可见,这种作用域也可以用category来实现.

声明一个类的Category,如 Object(Default),而这些方法的实现,可以在Object+Default.m中,也可以放在Object.m中,即如果这些方法的实现需要访问原有类内私有的内容,即需要在原有的类的实现文件中一起实现.而要使用这些对外私有,对内公有的方法时,直接引入这个Category的头文件Object+Default.h即可.

OC的泛型

优秀的语言都应该有泛型,来设计模板类和模板方法.在iOS9中的新特性中终于有了简单的泛型,是编译器层次的泛型.书写方法是在模板类声明时通过<ObjectType>来标记该类使用何种类型来填充容器,但是ObjectType 只能是OC对象,不能是基本数据类型.声明大致如下:

@interface MUQueue<__covariant ObjectType>  : NSObject

- (ObjectType)front;

- (void)push:(ObjectType) obj;
@end

由于类型只能是OC对象,所以在实现时,实现的方法实际上还是使用id作为参数.

- (id)front{

    return nil;
}

- (void)push:(id) obj{
    
}

还有两个相应的修饰符:

__covariant:协变性,子类型可以强转到父类型(里氏替换原则),如上代码显示.

__contravariant:逆变性,父类型可以强制转到子类型,不合理的属性.

对于方法的泛型,也提供了一个新的修饰符__kindof,如tableview中源码为:

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;

这就规定了该方法的参数或返回值必须是某个类或者其派生类.

这两个简单的泛型,最终都是转换为id属性来进行处理,那我们可以认为,实际上OC本身可能并没有做一些特别的处理来支持泛型,所以没有基础数据类型的泛型,而仅仅是通过在Xcode中进行处理,截获这些修饰符,给予一些编译提示罢了,因为最终的实现都是转换为id来处理的.

iOS读写文件

iOS中应用的文件读写只能在自己的沙盒内,所以有一个根目录,使用:NSHomeDirectory()来访问.在这个根目录下有4个文件夹:

  • Documents:主要的文件目录.
  • Application-Name,name为应用名称,这个目录是你的应用程序包,包括xib文件和本地化的一些资源.
  • Library目录下有两个目录Preferences目录和Caches目录,Preferences目录下放应用程序的使用偏好设置,Caches目录下房应用程序的缓存文件.
  • tmp供应用程序放临时文件.

获取应用中的各种路径:

NSHomeDirectory(); // 沙盒根目录.
[[NSBundle mainBundle] resourcePath]; //应用程序路径
NSTemporaryDirectory(); //tmp目录 
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPaths = [docPaths objectAtIndex:0];// documents目录
NSArray *cacPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [cacPaths objectAtIndex:0];// cache目录
NSArray *libPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryPath = [libPaths objectAtIndex:0];// library目录

iOS中文件操作通过 NSFileManager,文件读写通过NSFileHandle.

+ (NSFileManager *)defaultManager; // 获取基本文件管理器.
- (BOOL)createDirectoryAtPath:(NSString *)path withIntermediateDirectories:(BOOL)createIntermediates attributes:(nullable NSDictionary<NSString *, id> *)attributes error:(NSError **)error // 创建目录,创建目录时,多级目录也会同时创建出来,如 /a/b/c/d/e,直接一下创建5个文件夹.
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;// 复制文件
- (BOOL)moveItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;// 移动文件
- (BOOL)linkItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error;
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error;// 删除文件

- (BOOL)fileExistsAtPath:(NSString *)path;// 检测路径文件是否存在
- (BOOL)fileExistsAtPath:(NSString *)path isDirectory:(nullable BOOL *)isDirectory; // 检测路径文件或文件夹是否存在.
- (NSArray<NSString *> *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error; // 获取路径下全部文件.

NSdata中提供了简单的读写文件操作:

[data writeToFile:filePath atomically:NO];
NSData* reader =  [NSData dataWithContentsOfFile:filePath];

但这只是简单的文件读写,每次必须读写整个文件,对于文件的读写操作,OC通过NSFileHandle来处理:

+ (nullable instancetype)fileHandleForReadingAtPath:(NSString *)path;// 打开路径下的文件,只有读权限
+ (nullable instancetype)fileHandleForWritingAtPath:(NSString *)path;// 打开路径下的文件,只有写权限
+ (nullable instancetype)fileHandleForUpdatingAtPath:(NSString *)path;// 代开路径下的文件进行更新,有读写权限.
// 读写操作
- (NSData *)readDataToEndOfFile;// 读文件全部
- (NSData *)readDataOfLength:(NSUInteger)length;// 读到指定位置.

@property (readonly) unsigned long long offsetInFile;//当前偏移量
- (void)seekToFileOffset:(unsigned long long)offset;// 设置偏移量
- (unsigned long long)seekToEndOfFile;// 设置偏移量到文件的末尾
- (void)truncateFileAtOffset:(unsigned long long)offset;// 

- (void)writeData:(NSData *)data;// 写文件,写的开始在当前设置的偏移量位置.

iOS9之后,使用https.

强行使用http:在Info.plist中添加NSAppTransportSecurity类型Dictionary

NSAppTransportSecurity下添加NSAllowsArbitraryLoads类型Boolean,值设为YES

苹果之后将全面禁止http

ATS设置 :

<key>NSAppTransportSecurity</key>
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
</dict>

OC不定参数的写法

主要使用不定参数的地方是格式化字符串的输入,这里也只用格式化字符串来说明不定参数.

函数说明时:

- (void)log:(NSString *)format,... NS_FORMAT_FUNCTION(1,2);

后面的NS_FORMAT_FUNCTION(1,2)只是在编译器中检测格式化字符串的格式.值得注意的地方是,参数列表中,计数居然是从1开始.

在函数中获取这些参数值的写法如下:

va_list params;  //定义一个指向个数可变的参数列表指针;
id argument;

va_start(params, format);
// 在va_start和va_end中间获取参数列表.
NSString *formatStr = [[NSString alloc] initWithFormat:format locale:[NSLocale currentLocale] arguments:params];

//while ((argument = va_arg(params, id))) {//返回参数列表中指针arg_ptr所指的参数,返回类型为type,并使指针arg_ptr指向参数列表中下一个参数
//    [arr addObject:argument];
//}

va_end(params);//释放列表指针