iOS高级实用

iOS关键字(下):assign/retain/strong/n

2018-10-29  本文已影响28人  树懒啊树懒

目录:

(1)alloc/reatain/release/dealloc
(2)autoreleasepool/autorelease
(3)__strong/__weak/__unsafe_unretain/__autoreleasing
(4)assign/retain/copy/weak/strong
(5)assign/retain/strong等
(6)assign/retain/strong等
(7)const
(8)nil/NULL/Nil/NSNull/nan
(9)abs/fabs/fabsf等
(10)NSString格式化输出
(11)集合类

(6)assign/retain/strong等

synthesize关键字:

根据@property设置,自动生成成员变量相应的存取方法,从而可以使用点操作符来方便的存取该成员变量 。

@implementation:

@implementation 关键字,表明类的实现 @end 结束

self

self 关键字 :类似于java中的this,是隐藏参数,指向当前调用方法的类。

super

super 关键字 :调用父类的方法。

self = [super init] 这里不是判断self与[super init]是否相等,而是判断是否可以成功初始化。[super init]:父类初始化成功的话,通过=给self,这样self成为一个非空对象,整个来说即非false(非NO)。

import

" #import " 告诉预处理器,将头文件的内容包含到本文件中. OC 中的import 能保证头文件只会被包含一次 .

interface

@interface关键字:声明一个Student类。@end 结束声明.
冒号:表示继承 后面跟的是父类.

NSObject

NSObject是大多数对象都会用到的内存管理,和初始化框架,以及反射和类型操作. 相 当于Object。

NS缩写

NS是NextSTEP缩写,表示这个函数来自Cocoa工具包。

声明全局变量 , 与C中一样。

property

property关键字:设置成员变量的属性(有读/写,赋值assign,retain,copy ,以及对多线程的支持nonatomic)。

Function

声明一个方法,格式是
–(返回值) 方法关键字1 : (参数类型)参数名 方法关键字2 : (参数类型)参数名 …… (在读方法的时候就可以先找方法关键字来确定参数)。

'+' 和 '-' 号

' - '减号是实例方法, ' + ' 是类方法

Designated Initializer

另一个初始化方法中调用已有的初始化方法 这种概念被称为Designated Initializer.

NSLog

NSLog是OC中的标准输出, 附加输出当时日期, 时间, 应用程序名称 . 使用NSLog()输出任意对象的值时,都会使用%@格式说明。在使用这个说明符时,对象通过一个名为description的方法提供自己的NSLog()格式。

getter/setter

使用@property配合@synthesize可以让编译器自动实现getter/setter方法,使用的时候也很方便,可以直接使用“对象.属性”的方法调用;

如果我们想要”对象.方法“的方式来调用一个方法并获取到方法的返回值,那就需要使用@property配合@dynamic了

dynamic

使用@dynamic关键字是告诉编译器由我们自己来实现访问方法。如果使用的是@synthesize,那么这个工作编译器就会帮你实现了。

readonly

readonly此标记说明属性是只读的,默认的标记是读写,如果你指定了只读,在@implementation中只需要一个读取器。或者如果你使用@synthesize关键字,也是有读取器方法被解析。而且如果你试图使用点操作符为属性赋值,你将得到一个编译错误。

readwrite

readwrite此标记说明属性会被当成读写的,这也是默认属性。设置器和读取器都需要在@implementation中实现。如果使用@synthesize关键字,读取器和设置器都会被解析。

nonatomic

nonatomic:非原子性访问,对属性赋值的时候不加锁,多线程并发访问会提高性能。如果不加此属性,则默认是两个访问方法都为原子型事务访问。

atomic和nonatomic用来决定编译器生成的getter和setter是否为原子操作。

atomic

设置成员变量的@property属性时,默认为atomic,提供多线程安全。

在多线程环境下,原子操作是必要的,否则有可能引起错误的结果。加了atomic,setter函数会变成下面这样:

{lock}
  if (property != newValue) { 
          [property release]; 
          property = [newValue retain]; 
  }
{unlock}

nonatomic
禁止多线程,变量保护,提高性能。

atomic是Objc使用的一种线程保护技术,基本上来讲,是防止在写未完成的时候被另外一个线程读取,造成数据错误。而这种机制是耗费系统资源的,所以在iPhone这种小型设备上,如果没有使用多线程间的通讯编程,那么nonatomic是一个非常好的选择。

指出访问器不是原子操作,而默认地,访问器是原子操作。这也就是说,在多线程环境下,解析的访问器提供一个对属性的安全访问,从获取器得到的返回值或者通过设置器设置的值可以一次完成,即便是别的线程也正在对其进行访问。如果你不指定 nonatomic ,在自己管理内存的环境中,解析的访问器保留并自动释放返回的值,如果指定了 nonatomic ,那么访问器只是简单地返回这个值。

assign:

简单赋值,不更改索引计数
对基础数据类型 (例如NSInteger,CGFloat)和C数据类型(int, float, double, char, 等) 适用简单数据类型

此标记说明设置器直接进行赋值,这也是默认值。在使用垃圾收集的应用程序中,如果你要一个属性使用assign,且这个类符合NSCopying协议,你就要明确指出这个标记,而不是简单地使用默认值,否则的话,你将得到一个编译警告。这再次向编译器说明你确实需要赋值,即使它是可拷贝的。

copy:

建立一个索引计数为1的对象,然后释放旧对象

对NSString 它指出 : 在赋值时使用传入值的一份拷贝。拷贝工作由copy方法执行,此属性只对那些实行了NSCopying协议的对象类型有效。更深入的讨论,请参考“复制”部分。

retain:

释放旧的对象,将旧对象的值赋予输入对象,再提高输入对象的索引计数为1
对其他NSObject和其子类

对参数进行release旧值,再retain新值

指定retain会在赋值时唤醒传入值的retain消息。此属性只能用于Objective-C对象类型,而不能用于Core Foundation对象。(原因很明显,retain会增加对象的引用计数,而基本数据类型或者Core Foundation对象都没有引用计数——译者注)。

注意: 把对象添加到数组中时,引用计数将增加对象的引用次数+1。

retain的实际语法为:

- (void)setName:(NSString *)newName { 
    if (name != newName) { 
       [name release]; 
       name = [newName retain]; 
       // name’s retain count has been bumped up by 1 
    } 
}

copy与retain:

Copy其实是建立了一个相同的对象,而retain不是:

  1. copy:
    比如一个NSString对象,地址为0×1111,内容为@”STR”
    Copy到另外一个NSString之后,地址为0×2222,内容相同,新的对象retain为1,旧有对象没有变化

  2. retain:
    retain到另外一个NSString之后,地址相同(建立一个指针,指针拷贝),内容当然相同,这个对象的retain值+1
    也就是说,retain是指针拷贝,copy是内容拷贝。哇,比想象的简单多了…

retain的set方法应该是浅复制,copy的set方法应该是深复制了

copy另一个用法:

copy是内容的拷贝 ,对于像NSString,的确是这样.

但是,如果是copy的是一个NSArray呢?比如,

NSArray *array = [NSArray arrayWithObjects:@"hello",@"world",@"baby"];
NSArray *array2 = [array copy]; 

这个时候,系统的确是为array2开辟了一块内存空间,但是我们要认识到的是,array2中的每个元素,,只是copy了指向array中相对应元素的指针.这便是所谓的"浅复制".

assign与retain:

  1. 接触过C,那么假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。

此时a和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?

答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉。

  1. 了解到1中assign的问题,那么如何解决?

最简单的一个方法就是使用引用计数(reference counting),还是上面的那个例子,我们给那块内存设一个引用计数,当内存被分配并且赋值给a时,引用计数是1。当把a赋值给b时引用计数增加到2。这时如果a不再使用这块内存,它只需要把引用计数减1,表明自己不再拥有这块内存。b不再使用这块内存时也把引用计数减1。

当引用计数变为0的时候,代表该内存不再被任何指针所引用,系统可以把它直接释放掉。

总结:上面两点其实就是assign和retain的区别,assign就是直接赋值,从而可能引起1中的问题,当数据为int, float等原生类型时,可以使用assign。

retain就如2中所述,使用了引用计数,retain引起引用计数加1, release引起引用计数减1,当引用计数为0时,dealloc函数被调用,内存被回收。

assign retain copy 简析

NSString *pt = [[NSString alloc] initWithString:@"abc"];

上面一段代码会执行以下两个动作

  1. 在堆上分配一段内存用来存储@"abc" 比如:内存地址为:0X1111 内容为 "abc"
  1. 在栈上分配一段内存用来存储pt 比如:地址为:0Xaaaa 内容自然为0X1111

下面分别看下assign retain copy

1. assign的情况:

NSString *newPt = [pt assign];
此时newPt和pt完全相同 地址都是0Xaaaa 内容为0X1111 即newPt只是pt的别名,对任何一个操作就等于对另一个操作。 因此retainCount不需要增加。

2. retain的情况:

NSString *newPt = [pt retain];
此时newPt的地址不再为0Xaaaa,可能为0Xaabb 但是内容依然为0X1111。 因此newPt 和 pt 都可以管理"abc"所在的内存。因此 retainCount需要增加1

3. copy的情况:

NSString *newPt = [pt copy];
此时会在堆上重新开辟一段内存存放@"abc" 比如0X1122 内容为@"abc 同时会在栈上为newPt分配空间 比如地址:0Xaacc 内容为0X1122 因此retainCount增加1供newPt来管理0X1122这段内存

(7)const

const

1.没有const修饰的指针

指针p和*p都能被修改

// 定义一个指针变量
int *p = NULL;
 
// 定义2个int类型的变量
int a = 10;
int b = 30;
 
// p指向a
p = &a;
*p = 20;
 
// p指向b
p = &b;
*p = 40;
 
NSLog(@"%d %d", a, b);

2.const修饰的*p

被const修饰的*p只能被赋值一次,以后不能赋值,否则编译器报错

// const修饰的*p
const int *p = NULL;
int const *p = null;
 
*p = 20; // 编译器报错,不能修改*p的值

3.const修饰的p

被const修饰的p只能存一次地址,以后再也不能其它存地址了,否则编译器报错

// const修饰的指针变量p
int * const p = NULL;
int a = 20;
p = &a; // 编译器报错,不能修改指针变量p
  1. const在声明字符串的用法

NSString * const ZMJName = @"jack";

static和const联合使用

static将一个全局变量变成局部变量
const将一个局部变量变成局部常量

// 定义了一个局部常量
static const CGFloat ZMJRed = 0.4;
static const CGFloat ZMJGreen = 0.6;
static const CGFloat ZMJBlue = 0.7;

使用static const 与 #define

使用static const修饰变量和宏定义的比较
1. 相同点
都不能再被修改
一处修改,其它都改了
2. 不同点
static const修饰变量只有一份内存
宏定义,只是简单的替换,每次使用都需要创建一份内存
结论
使用static const修饰更加高效,在同一个文件内可以使用static const取代#define

// static const修饰变量只有一份内存
static const CGFloat ZMJRed = 0.4;
     
// 宏定义,只是用0.4替换ZMJRed,每次使用都需要创建一份内存
#define ZMJRed 0.4

const实际开发的应用

一般会先新建ZMJConst.h文件专门存放常量的引用

// 引用某变量,如果没有使用const修饰,就不能直接在编译的时候就能检测是否修改了ZMJRed
extern const CGFloat ZMJRed;
extern NSString * const ZMJName;

可以模仿系统UIKIT_EXTERN来代替extern,逼格更高!
UIKIT_EXTERN const CGFloat ZMJRed;
UIKIT_EXTERN NSString * const ZMJName;

一般会新建ZMJConst.m文件专门存放static const修饰的变量,需要用的时候导入头文件就可以了。

// 定义了整个程序都能访问的常量
const CGFloat ZMJRed = 0.4;
NSString * const ZMJName = @"jack";

const与宏的区别(面试题):

const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量。

1.编译时刻:宏是预编译(编译之前处理),const是编译阶段。
2.编译检查:宏不做检查,不会报编译错误,只是替换,const会编译检查,会报编译错误。

3.宏的好处:宏能定义一些函数,方法。 const不能。
4.宏的坏处:使用大量宏,容易造成编译时间久,每次都需要重新替换。

注意:很多Blog都说使用宏,会消耗很多内存,我这验证并不会生成很多内存,宏定义的是常量,常量都放在常量区,只会生成一份内存。

    // 常见的常量:抽成宏
    #define XMGAccount @"account"
     
    #define XMGUserDefault [NSUserDefaults standardUserDefaults]
     
    // 字符串常量
    static NSString * const account = @"account";
     
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        // 偏好设置存储
        // 使用宏
        [XMGUserDefault setValue:@"123" forKey:XMGAccount];
     
        // 使用const常量
        [[NSUserDefaults standardUserDefaults] setValue:@"123" forKey:account];
     
    }

const作用:限制类型

1.const仅仅用来修饰右边的变量(基本数据变量p,指针变量*p)
2.被const修饰的变量是只读的。
const基本使用
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        // 定义变量
        int a = 1;
     
        // 允许修改值
        a = 20;
     
        // const两种用法
        // const:修饰基本变量p
        // 这两种写法是一样的,const只修饰右边的基本变量b
        const int b = 20; // b:只读变量
        int const b = 20; // b:只读变量
     
        // 不允许修改值
        b = 1;
     
        // const:修饰指针变量*p,带*的变量,就是指针变量.
        // 定义一个指向int类型的指针变量,指向a的地址
        int *p = &a;
     
        int c = 10;
     
        p = &c;
     
        // 允许修改p指向的地址,
        // 允许修改p访问内存空间的值
        *p = 20;
     
        // const修饰指针变量访问的内存空间,修饰的是右边*p1,
        // 两种方式一样
        const int *p1; // *p1:常量 p1:变量
        int const *p1; // *p1:常量 p1:变量
     
        // const修饰指针变量p1
        int * const p1; // *p1:变量 p1:常量
     
     
        // 第一个const修饰*p1 第二个const修饰 p1
        // 两种方式一样
        const int * const p1; // *p1:常量 p1:常量
     
        int const * const p1;  // *p1:常量 p1:常量
     
    }

const开发中使用场景:

1.需求1:提供一个方法,这个方法的参数是地址,里面只能通过地址读取值,不能通过地址修改值
2.需求2:提供一个方法,这个方法的参数是地址,里面不能修改参数的地址。
    @implementation ViewController
     
    // const放*前面约束参数,表示*a只读
    // 只能修改地址a,不能通过a修改访问的内存空间
    - (void)test:(const int * )a
    {
    //    *a = 20;
    }
     
    // const放*后面约束参数,表示a只读
    // 不能修改a的地址,只能修改a访问的值
    - (void)test1:(int * const)a
    {
        int b;
        // 会报错
        a = &b;
     
        *a = 2;
    }
     
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
     
        int a = 10;
     
        // 需求1:提供一个方法,这个方法的参数是地址,里面只能通过地址读取值,不能通过地址修改值。
     
        // 这时候就需要使用const,约束方法的参数只读.
        [self test:&a];
     
        // 需求2:提供一个方法,这个方法的参数是地址,里面不能修改参数的地址。
        [self test1:&a];
    }
    @end

static和extern简单使用(要使用一个东西,先了解其作用)

static作用:

    修饰局部变量:

    1.延长局部变量的生命周期,程序结束才会销毁。

    2.局部变量只会生成一份内存,只会初始化一次。

    3.改变局部变量的作用域。

    修饰全局变量

    1.只能在本文件中访问,修改全局变量的作用域,生命周期不会改

    2.避免重复定义全局变量

extern作用:
    只是用来获取全局变量(包括全局静态变量)的值,不能用于定义变量
extern工作原理:
    先在当前文件查找有没有全局变量,没有找到,才会去其他文件查找。
    // 全局变量:只有一份内存,所有文件共享,与extern联合使用。
    int a = 20;
     
    // static修饰全局变量
    static int age = 20;
     
    - (void)test
    {
        // static修饰局部变量
        static int age = 0;
        age++;
        NSLog(@"%d",age);
    }
     
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        [self test];
        [self test];
     
        extern int age;
        NSLog(@"%d",age);
    }

static与const联合使用

static与const作用:声明一个只读的静态变量
开发使用场景:在一个文件中经常使用的字符串常量,可以使用static与const组合

// 开发中常用static修饰全局变量,只改变作用域
 
// 为什么要改变全局变量作用域,防止重复声明全局变量。
 
// 开发中声明的全局变量,有些不希望外界改动,只允许读取。
 
// 比如一个基本数据类型不希望别人改动
 
// 声明一个静态的全局只读常量
static const int a = 20;
 
// staic和const联合的作用:声明一个静态的全局只读常量
 
// iOS中staic和const常用使用场景,是用来代替宏,把一个经常使用的字符串常量,定义成静态全局只读变量.
 
// 开发中经常拿到key修改值,因此用const修饰key,表示key只读,不允许修改。
static  NSString * const key = @"name";
 
// 如果 const修饰 *key1,表示*key1只读,key1还是能改变。
 
static  NSString const *key1 = @"name";

extern与const联合使用

开发中使用场景:在多个文件中经常使用的同一个字符串常量,可以使用extern与const组合。
原因:
    static与const组合:在每个文件都需要定义一份静态全局变量。
    extern与const组合:只需要定义一份全局变量,多个文件共享。
全局常量正规写法:开发中便于管理所有的全局变量,通常搞一个GlobeConst文件,里面专门定义全局变量,统一管理,要不然项目文件多不好找。

GlobeConst.h
    extern NSString * const nameKey = @"name";
     
GlobeConst.m
    #import <Foundation/Foundation.h>
  
    NSString * const nameKey = @"name";

(8)nil/NULL/Nil/NSNull/nan

一 : 定义

nil

定义一个空的实例,指向OC中对象的空指针。

示例代码:

 NSString *someString = nil;
 NSURL *someURL = nil;
 id someObject = nil;
 if (anotherObject == nil) // do something

用法讲解:

当对某个对象release 的同时最好把他们赋值为nil,这样可以确保安全性,如果不赋值nil,可能导致程序崩溃.

NSArray * array = [NSArray arrayWithObjects:@"test",@"test1" ,nil];
[array release];
if (array) {
      //仅仅对数组release,并没有赋值为nil,在程序某个地方如果继续对数组操作,程序直接崩溃
      NSString * string = [array objectAtIndex:0];
      NSLog(@"%@",string);
}

NULL

NULL可以用在C语言的各种指针上。

 #define __DARWIN_NULL 
 #define__DARWIN_NULLConstants
 示例代码:
 int *pointerToInt = NULL;    
 char *pointerToChar = NULL;  
 struct TreeNode *rootNode = NULL;

用法讲解:
在Objective-C里,nil对象被设计来跟NULL空指针关联的。他们的区别就是nil是一个对象,而NULL只是一个值。而且我们对于nil调用方法,不会产生crash或者抛出异常。

Nil

定义一个空的类

示例代码:

 Class someClass = Nil; 
 Class anotherClass = [NSString class];

NSNull

NSNull是一个类,它定义了一个单例对象用于表示集合对象的空值

集合对象无法包含nil作为其具体值,如NSArray、NSSet和NSDictionary。相应地,nil值用一个特定的对象
NSNull来表示。NSNull提供了一个单一实例用于表示对象属性中的的nil值。默认的实现方法中,
dictionaryWithValuesForKeys:和setValuesForKeysWithDictionary:自动地将 NSNull和nil相互转换,因此您的对象不需要进行NSNull的测试操作。

NAN

比如当进行除法运算时,分母为0,就会导致结果为NAN.

nan的意思就是not a number。

主要常见原因:

解决方法除了排除根源所在之外,用函数isnan()也是不错的选择(至少在没有彻底解决以前)
如下

float _x = NAN;
if (!isnan(_x)) {
cell.imgView.frame = CGRectMake(_x, 8, 10, 12);
}”

CGFloat adScale   = newsData.FHEIGHT / newsData.FWIDTH;
if (isnan(adScale)) {
   adScale = 0;
}

二 : 为空的判断

在开发中,会遇到很多空的情况,有时候取得对象(null),还有时候会得到<null>的情况,我们需要判断是否为空,进行return;
id result;

针对(nil)这种情况
if(result == nil) return;

针对<null>的情况
if([result isEqual:[NSNull null]]) return;

前者的判断,我们用的比较频繁,但后者,用的比较少,一般赋值给nil之外的其他空值;

基本数据类型的空值判断:

(1)对于类似于NSString类型的数据进行空值判断
NSString *string;
1、string == nil;
2、[string isEqualToString:@""];

(2)对于类似于NSNumber类型的数据进行空值判断
NSNumber *number;
1、number == nil;
2、[number isEqualToNumber:@0]

(3)对于类似于NSArray类型的数据进行空值判断
NSArray *array;
1、array == nil;
2、array.count = 0;

(4)对于类似于NSDictionary类型的数据进行空值判断
NSDictionary *dictionary;
1、[dictionary isEqual:[NSNull null]];
2、dictionary.count = 0;

(5)对于对象类型的数据进行空值判断
可以使用 !(非) 来判断
if(!class) return;

(9)abs/fabs/fabsf等

abs 绝对值

int abs(int i); // 处理int类型的取绝对值

fabs 绝对值

double fabs(double i); //处理double类型的取绝对值

fabsf 绝对值

float fabsf(float i); /处理float类型的取绝对值

ceilf 进位方法

float numberToRound;
int result;
numberToRound = 5.61;
result = (int)ceilf(numberToRound);
NSLog(@"ceilf(%.2f) = %d", numberToRound, result);
//输出 ceilf(5.61) = 6

numberToRound = 5.41;
result = (int)ceilf(numberToRound);
NSLog(@"ceilf(%.2f) = %d", numberToRound, result);
//输出 ceilf(5.41) = 6

roundf 四舍五入

float numberToRound;
int result;
numberToRound = 5.61;
result = (int)roundf(numberToRound);
NSLog(@"roundf(%.2f) = %d", numberToRound, result);
//输出 roundf(5.61) = 6

numberToRound = 5.41;
result = (int)roundf(numberToRound);
NSLog(@"roundf(%.2f) = %d", numberToRound, result);
//输出 roundf(5.41) = 5

floorf 模位方法

float numberToRound;
int result;
numberToRound = 5.61;
result = (int)floorf(numberToRound);
NSLog(@"floorf(%.2f) = %d", numberToRound, result);
//输出 floorf(5.61) = 5

numberToRound = 5.41;
result = (int)floorf(numberToRound);
NSLog(@"floorf(%.2f) = %d", numberToRound, result);
//输出 floorf(5.41) = 5

(10)NSString格式化输出

格式:

%@ 对象

%d, %i 整数

%u 无符整形

%f 浮点/双字

%x, %X 二进制整数

%o 八进制整数

%zu size_t

%p 指针

%e 浮点/双字 (科学计算)

%g 浮点/双字

%s C 字符串

%.*s Pascal字符串

%c 字符

%C unichar

%lld 64位长整数(long long)

%llu 无符64位长整数

%Lf 64位双字

文档

表1: NSString格式化方法和CFString格式化函数支持的格式说明符定义说明

%@

Objective-C对象,打印为descriptionWithLocale返回的字符串:如果可用,或其他描述。也适用于CFTypeRef对象,返回CFCopyDescription函数的结果。

%%

'%'字符

%d,%D,%i

有符号32位整数(int)

%u,%U

无符号32位整数(unsigned int)

%hi

有符号16位整数(短)

%hu

无符号16位整数(无符号短整数)

%qi

签名64位整数(long long)

%qu

无符号64位整数(unsigned long long)

%x

无符号32位整数(unsigned int),使用数字0-9和小写a-f以十六进制打印

%X

无符号32位整数(unsigned int),使用数字0-9和大写A-F以十六进制打印

%qx

无符号64位整数(无符号长long),使用数字0-9和小写a-f以十六进制打印

%qX

无符号64位整数(无符号长long),使用数字0-9和大写A-F以十六进制打印

%o,%O

无符号32位整数(unsigned int),以八进制打印

%f

64位浮点数(双精度)

%e

64位浮点数(双精度),使用小写e以科学计数法打印以引入指数

%E

64位浮点数(双精度数),用科学计数法打印,使用大写E表示指数

%g

64位浮点数(double),如果指数小于-4或大于或等于精度,则以%e的样式打印,格式为%f否则

%G

64位浮点数(双精度),如果指数小于-4或大于或等于精度,则以%E的样式打印,格式为%f否则

%c

8位无符号字符(unsigned char),由NSLog()作为ASCII字符打印,如果不是ASCII字符,则以八进制格式\ ddd或Unicode十六进制格式\ udddd打印,其中d为a数字

%C

16位Unicode字符(unichar),由NSLog()作为ASCII字符打印,或者,如果不是ASCII字符,则以八进制格式\ ddd或Unicode十六进制格式\ udddd打印,其中d是数字

%s

以空值终止的8位无符号字符数组。 %s以系统编码解释其输入,而不是例如UTF-8。

%S

以16位Unicode字符为单位的空终止数组

%p

Void指针(void *),以十六进制打印,数字为0-9,小写字母为a-f,前导0x

%L

长度修饰符指定后续的a,A,e,E,f,F,g或G转换说明符适用于long double参数

%a

64位浮点数(双精度),用科学计数法打印,带小数点前面的前导0x和一个十六进制数字,使用小写p引入指数

%A

一个64位浮点数(双精度数),用科学计数法打印,在小数点前有一个前导0X和一个十六进制数字,用大写字母P来表示指数

%F

64位浮点数(双精度),以十进制表示法打印

%z

长度修饰符,指定后续的d,i,o,u,x或X转换说明符适用于size_t或相应的有符号整数类型参数

%t

Length修饰符,指定后续的d,i,o,u,x或X转换说明符适用于ptrdiff_t或相应的无符号整数类型参数

%j

长度修饰符,指定后续的d,i,o,u,x或X转换说明符适用于intmax_t或uintmax_t参数

平台依赖
Mac OS X使用多种数据类型-NSInteger,NSUInteger,CGFloat和CFIndex-来提供在32位和64位环境中表示值的一致方法。在32位环境中,NSInteger和NSUInteger分别定义为int和unsigned int。在64位环境中,NSInteger和NSUInteger分别定义为long和unsigned long。为了避免根据平台使用不同的printf样式类型说明符,可以使用表2中所示的说明符。请注意,在某些情况下,您可能必须转换值。

表2: 数据类型的格式说明符类型定义建议

NSInteger

%ld或%lx
将值强制转换为long

NSUInteger

%lu或%lx
将值转换为unsigned long

CGFloat

%f或%g%f
适用于格式化时的浮点数和双精度数;但扫描时请看下面的警告

CFIndex

%ld或%lx
与NSInteger相同

pointer

%p
%p将0x添加到输出的开头。如果您不想这样,请使用%lx并强制转换为long。

long long

%lld或%llx
long long在32位和64位平台上都是64位
在32位和64位平台上,

unsigned long long

%llu或%llx
unsigned long long是64位

以下示例说明了使用%ld格式化NSInteger和使用强制转换。

NSInteger i = 42;
printf(“%ld \ n”,(long)i);

除了表2中提到的注意事项外,还有一个额外的扫描案例:您必须区分float和double的类型。你应该使用%f表示float,%lf表示double。如果你需要使用scanf(或那里的变种)

示例:

1.不足几位前补0

%02表示不足2位补0

int   hour  =  4;
endHour.text  =  [NSString stringWithFormat:@"%02d",hour];
//结果是04

2.小数保留位数

.2f表示要保留小数位数

float   hour  =  4.12345;
endHour.text  =  [NSString stringWithFormat:@"%.2f",hour];
//结果是4.12

(11)集合类

在Objective-C中的集合类中主要包括不可变的数组–NSArray, 可变的数组–NSMutableArray, 不可变的字典–NSDictionary, 可变的字典–NSMutableDictionary, 不可变的集合–NSSet,可变的集合–NSMutableSet。

一、数组

1.不可变的数组 NSArray

数组是有序对象的集合,用来存储对象的有序列表,在OC中数组中必须存的是对象,不能是基本数据类型,若想存入基本数据类型,必须先把数据类型转换成对象,然后再存入集合类中。 NSArray也是OC中的一个对象,使用NSArray也需要进行实例化,前面的博客在类的初始化中提到了便利初始化函数和便利构造器,在NSArray中也少不了这两样东西我们可以通过NSArray的便利初始化函数或者便利构造器进行NSArray的实例化和初始化工作。

(1)NSArray的初始化

NSArray的便利初始化函数: -(id) initWithObjects : (id) firstObject, ….;

NSArray的便利构造器: +(id) arrayWithObjects: (id) firstObject, …;

demo:

//NSArray的便利初始化
NSArray *array1 = [[NSArray alloc] initWithObjects:@"aaa", @"bbb", @"ccc", nil];
//NSArray的便利构造器
NSArray *array2 = [NSArray arrayWithObjects:@"111", @"222", @"333", nil];

(2)获取数组元素的个数和数组的元素

-(NSUInteger) count; 获取数组元素的个数

-(id) objectAtIndex : (NSUInteger) index;

demo:

//获取数组元素的个数和元素
int count = (int)[array1 count];
//获取相应索引的元素
id element = [array1 objectAtIndex:0];
NSLog(@"array1_count = %d, array[0] = %@", count, element);

//获取数组元素的个数和元素
int count = (int)[array1 count];
//获取相应索引的元素
id element = [array1 objectAtIndex:0];
NSLog(@"array1_count = %d, array[0] = %@", count, element);

运行结果:
Objective-C
2014-08-01 16:25:37.117 HelloOC[1285:303] array1_count = 3, array[0] = aaa
1

2014-08-01 16:25:37.117 HelloOC[1285:303] array1_count = 3, array[0] = aaa

(3)有了count和objectAtIndex我们就可以对数组进行元素的遍历啦

demo:

//封装遍历数组的函数
void array_display(id array)
{
    for (int i = 0 ; i
        id temp = [array objectAtIndex:i];
        NSLog(@"%@", temp);
    }
}

2.可变数组:NSMutableArray

NSArray的容量是固定的,而NSMutableArray的容量是可变的,我们可以在NSMutableArray实例化的时候先初始化一个容量,不过这个容量不是固定的,不够的时候会自动增加。NSMutableArray是NSArray的子类,是对NSArray的一个扩充。

(1) NSMutableArray的初始化

NSMutableArray的便利初始化函数:-(id) initWithCapacity : (NSUInteger) numItems;

NSMutableArray的便利构造器:+(id) arrayWithCapacity: (NSUInteger) numItems;

demo:
Objective-C

//NSMutableArray的便利初始化
NSMutableArray *array3 = [[NSMutableArray alloc] initWithCapacity:3];
//NSMutableArray的便利构造器
NSMutableArray *array4 = [NSMutableArray arrayWithCapacity:3];

(2)元素的添加和删除

增加元素: -(void) addObject: (id) anObject; –往数组的尾部添加元素

删除全部内容: -(void) removeAllObjects;

删除最后一个元素: -(void) removeLastObject;

通过索引删除元素: -(void) removeObjectAtIndex: (NSUInteger) index;

删除任意一个元素: -(void) removeObject : (id)object;

代码如下:
Objective-C

//初始化NSMutableArray
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"111", @"222", @"333", nil];
//添加元素
[array addObject:@"444"];

//移除元素
[array removeObject:@"111"];

//移除最后一个元素
[array removeLastObject];

//按照索引删除元素
[array removeObjectAtIndex:0];

array_display(array);

运行结果:
Objective-C
2014-08-01 17:18:58.658 HelloOC[1467:303] 333
1

2014-08-01 17:18:58.658 HelloOC[1467:303] 333

(3)元素的插入和替换

通过数组的索引来进行元素的替换:-(void) replaceObjectAtIndex :(NSUInteger) index withObject: (id) anObject;

在指定索引处插入对象:-(void) insertObject : (id) anObject atIndex : (NSUInteger) index;

demo:
Objective-C

//初始化NSMutableArray
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"111", @"222", @"333", nil];

//对指定位置的元素进行替换
[array replaceObjectAtIndex:0 withObject:@"aaa"];

//在相应的位置插入元素
[array insertObject:@"bbb" atIndex:1];

array_display(array);

运行结果:
Objective-C
2014-08-01 17:44:20.769 HelloOC[1520:303] aaa
2014-08-01 17:44:20.769 HelloOC[1520:303] bbb
2014-08-01 17:44:20.770 HelloOC[1520:303] 222
2014-08-01 17:44:20.770 HelloOC[1520:303] 333

4.数组的遍历

上面已经写了一种数组遍历方式,下面总结了其他的遍历方式,用枚举器(类似Java中的迭代器)和快速枚举(类似PHP中的foreach)

(1)枚举器(迭代器)的用法

要通过NSEnumerator来遍历数组首先得通过-(NSEnumerator *) objectEnumerator来得到枚举器;通过枚举其中的nextObject来获取对象

demo:如下
Objective-C

//迭代遍历数组
//获取枚举器
NSEnumerator *enumerator = [array objectEnumerator];
//临时变量
id obj;
while (obj = [enumerator nextObject]) {
    NSLog(@"%@", obj);
}

(2)快速遍历法,相当于PHP中的foreach的用法
Objective-C

for(id obj in array)
{
    NSLog(@"%@", obj);
}

5.对可变数组进行排序
Objective-C

//对数组进行排序
[arraymul sortUsingComparator:^NSComparisonResult(NSString *obj1, NSString *obj2)
{
    if ([obj1 compare:obj2] 0)
    {
        NSLog(@"交换");
        return 1;
    }
    else
    {
        NSLog(@"不交换");
        return -1;
    }
}];

二、字典(Dictionary)

字典就像java中的Map, 里面存的是键值对,key的值是不可以重复的。字典有可改变的和不和不可改变之分

1.不可变字典NSDictionary

(1)字典的创建和获取字典的值
Objective-C

//不可变字典的初始化
NSDictionary *dictionay = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"key1", @"value2", @"key2", nil];
id value = [dictionay objectForKey:@"key1"];
NSLog(@"key1 => %@", value);

代码运行结果:
Objective-C
2014-08-01 19:09:41.319 HelloOC[1658:303] key1 => value1

(2).字典的遍历

字典的遍历方法是通过字典里的-(NSArray *)allKeys获取所有的key存入数组中然后通过遍历数组获取key对应的value,封装成函数如下:
Objective-C

//封装遍历字典的函数
void dictionary_display(id dictionay)
{
    NSArray *keys = [dictionay allKeys];
    for (id obj in keys) {
        NSLog(@"%@ => %@", obj, [dictionay objectForKey:obj]);
    }
}

2.可变字典NSMutableDictionary

在使用可变字典NSMutableDictionary时可以先给可变字典初始化一个空间,如果不够的话会自动增加

给可变字典分配初始化空间: +(id) dictionaryWithCapacity : (NSUInteger) num;

给可变字典添加对象:-(void) setObject(id) obj forKey(id) aKey;

根据关键字删除对象:-(void) removeObjectForKey : (id) aKey;

删除字典中的所有数据: -(void) removeAllObjects;

代码如下:
Objective-C

NSMutableDictionary *mulDictionary = [NSMutableDictionary dictionaryWithCapacity:3];
//添加值
[mulDictionary setObject:@"value1" forKey:@"key1"];
[mulDictionary setObject:@"value2" forKey:@"key2"];
[mulDictionary setObject:@"value3" forKey:@"key3"];
[mulDictionary setObject:@"value4" forKey:@"key4"];
//通过键删除值
[mulDictionary removeObjectForKey:@"key4"];
dictionary_display(mulDictionary);

运行结果:
Objective-C
2014-08-01 20:28:37.504 HelloOC[1786:303] key3 => value3
2014-08-01 20:28:37.504 HelloOC[1786:303] key1 => value1
2014-08-01 20:28:37.504 HelloOC[1786:303] key2 => value2

三.Set集合

​Set集合类就像我们数学中的集合一样是无序和不重复的,Set中也只能存放对象,也分为可变集合NSMutableSet和不可变集合NSSet。

​####1.不可变集合NSSet

​(1)NSSet的初始化和其他集合类一样也有其对应的便利初始化方法和便利构造器,

​便利初始化方法:-(id) initWithObjects: (id) firstObject, …;

​便利构造器:+(id) setWithObjects: (id) firstObject, …;

​(2)NSSet中也有-(NSUInteger) count 来获取元素的对象

​(3) 获取集合中的元素

​获取全部的集合元素,以数组的形式返回:-(NSArray *) allObjects;

​随进获取集合中的元素:-(id) anyObject;

​(4)判断两个Set是否相同 : – (BOOL) isEqualToSet: (NSSet *) otherSet;

​(5)判断一个元素是否在本集合中 -(BOOL) member: (id) obj;


Demo: ​
Objective-C

//set集合的操作
//便利初始化函数
NSSet *set1 = [[NSSet alloc] initWithObjects:@"aa", @"bb", @"cc", @"dd", nil];
//便利构造器
NSSet *set2 = [NSSet setWithObjects:@"AA", @"BB", @"CC", nil];

//获取集合中元素的个数
int count = (int) [set1 count];
NSLog(@"set1里面的元素的个数为:%d", count);

//遍历集合:把set集合转换为数组然后进行遍历
NSArray *setToArray = [set2 allObjects];
array_display(setToArray);

//随机获取Set中元素
id element = [set1 anyObject];
NSLog(@"随机获取其中的值%@", element);

//比较两个Set是否相等
if ([set1 isEqualToSet:set2] == NO) {
    NSLog(@"set1 != set2");
}

//查看一个元素是否在一个set中
if ([set1 member:@"aa"]) {
    NSLog(@"aa 在set1中");
}

​运行结果为:
Objective-C
2014-08-02 09:47:39.554 HelloOC[608:303] set1里面的元素的个数为:4
2014-08-02 09:47:39.555 HelloOC[608:303] CC
2014-08-02 09:47:39.555 HelloOC[608:303] AA
2014-08-02 09:47:39.555 HelloOC[608:303] BB
2014-08-02 09:47:39.555 HelloOC[608:303] 随机获取其中的值cc
2014-08-02 09:47:39.556 HelloOC[608:303] set1 != set2
2014-08-02 09:47:39.556 HelloOC[608:303] aa 在set1中

​2、可变Set: NSMutableSet

​1.可变集合的实例化和初始化

​便利初始化函数: -(id) initWithCapacity :(NSUInteger) numItems;

​便利构造器:+(id) setWithCapacity: (NSUInteger) numItems;

​2.往可变集合中添加元素

​-(void) addObject : (id) object;

​3.删除集合中的对象

​-(void) removeAllObjects; 删除所有的对象;

​-(void) removeObjects: (id) object 删除其中某一个对象;


demo:
Objective-C

//set可变集合
//便利初始化函数分配大小
NSMutableSet *mutableSet1 = [[NSMutableSet alloc] initWithCapacity:3];
NSMutableSet *mutableSet2 = [NSMutableSet setWithCapacity:3];

//添加元素
[mutableSet1 addObject:@"aaa"];
[mutableSet1 addObject:@"BBB"];
[mutableSet1 addObject:@"bbb"];

//删除元素
[mutableSet1 removeObject:@"BBB"];

//遍历Set
NSArray *setArray = [mutableSet1 allObjects];
array_display(setArray);

​ ​运行结果:
Objective-C
2014-08-02 10:18:34.844 HelloOC[701:303] aaa
2014-08-02 10:18:34.844 HelloOC[701:303] bbb

四:把基本数据类型包装成对象

​ ​前面不止一次的提到在OC中的集合类中是不能放基本数据类型的,那么我们如何把基本数据类型封装成对象呢? 在OC中给我们提供啦一个类专门来把基本数据类型封装成对象,这个类就是NSNumber.

​ ​ ​1. NSNumber的用法如下

​    ​    ​    ​把基本类型包装成对象的便利构造函数

​    ​    ​    ​    ​-(id) initWithChar : (char) value;

​    ​    ​    ​    ​-(id) initWithInt : (int) value;

​    ​    ​    ​    ​-(id) initWithFloat : (float) value;

​    ​    ​    ​    ​-(id) initWithBool: (BOOL) value;

​    ​    ​    ​把基本数据类型包装成对象的便利构造器

​    ​    ​    ​    ​+(id) numberWithChar : (char) value;

​    ​    ​    ​    ​+(id) numberWithInt : (int) value;

​    ​    ​    ​    ​+(id) numberWithFloat : (float) value;

​    ​    ​    ​    ​+(id) numberWithBool : (BOOL) value;

​    ​    ​    ​从NSNumber中获取值

​    ​    ​    ​    ​-(char) charValue;    ​    ​-(int) intValue;      ​-(float) floatValue;    ​ (BOOL) boolValue;    ​(NSString *) stringValue;

​2、在集合类中是不可以存储nil(空的),因为nil作为nil的结束符,那么我们如何来存储空对象呢?该NSNull出场啦,其功能是把null包装成对象.

​ ​ ​ ​ ​+(NSNull *) null;

上一篇下一篇

猜你喜欢

热点阅读