Objective-C语法点

OC特点

OC没有命名空间,对象的成员变量是默认受保护的。

弱类型语言,单继承

init系统自带的初始化方法,默认是给基本数据类型赋值0,0.0,指针赋值NULL,对象指针类型赋值nil 本质上都是(void *)0

[CLASS new]

[[CLASS alloc]]init]

[类型/对象 方法]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
重写init
-(instancetype)init{
// self若为nil说明生成实例失败
if(self = [super init]){
初始化代码
}
return self;
}
//带参构造函数
-(instancetype)initWithName:(NSString *)name andAge:(int)age{
...
}

//调用
类名 *p = [[类名 alloc] init]; // [类名 new];
类名 *p = [[类名 alloc] initWithName: @"xx" andAge:18];

多参数方法 (标签)
//实际函数名 setName: age: height:
- (void)setName:(NSString*)name age:(NSInteger)myAge height:(NSInteger)myHeight;
[classname setName:@"zz" age:18 height:1];

实例方法(对象方法)
-(返回值类型) 方法名:(参数类型)参数名:(参数类型)参数名 ; or {}
_age;
setAge();
getAge();

类方法
+ ()函数名:(参数类型)参数; 类似与C++static类静态函数 ,而OC中static只能修饰局部变量

【待补充】实例对象、类对象(class object)与元类对象(metaclass object)

实例对象:当我们在代码中new一个实例对象时,拷贝了实例所属的类的成员变量,但不拷贝类定义的方法。调用实例方法时,根据实例的isa指针去寻找方法对应的函数指针。

类对象:是一个功能完整的对象。特殊之处在于它们是由程序员定义而在运行时由编译器创建的,它没有自己的实例变量(这里区别于类的成员变量,他们是属于实例对象的,而不是属于类对象的,类方法是属于类对象自己的),但类对象中存着成员变量与实例方法列表。

元类对象:OC 的类方法是使用元类的根本原因,因为其中存储着对应的类对象调用的方法即类方法。其他时候都倾向于隐藏元类,因此真实世界没有人发送消息给元类对象。元类的定义和创建看起来都是编译器自动完成的,无需人为干涉。要获取一个类的元类,可使用如下定义的函数:

1
Class objc_getMetaClass(const char* name); //name为类的名字

此外还有一个获取对象所属的类的函数:

1
Class object_getClass(id obj) ;

由于类对象是元类的实例,所以当传入的参数为类名时,返回的就是指向该类所属的元类的指针。

参考文章:

对象与元类对象

清晰理解Objective-C元类

【待补充】消息机制

Runtime –对象、类对象、元类对象&消息传递机制

iOS面试题笔记

总结取到类在代码段中的地址的方式.

1.调试查看对象的isa指针的值.

2.在类方法中查看self的值.(相当于C++的this, 但在对象方法【动态分配内存,调用完成后会释放内存】中,self不可以以调用类方法,因为此时self代表对象;在类方法【在应用程序开始运行时一直驻于内存】中,self代表类,不可以调用对象方法与属性,只能通过实例化一个对象来调用对象方法与属性。可以总结:在实例方法中self是object,而在类方法中self是class,所以在两种方法内部无法通过self来相互调用,self只能调用相同类型的方法。)

3.调用对象的对象方法class 就会返回这个对象所属的类在代码段中的地址.

4.调用类的类方法class 就会返回这个类在代码段中的地址.

1
2
3
Class c1 = [类名 class];  //此时 c1 等价于 该类,只能调用该类的类方法
Class c2 = [类的实例化对象 class];
NSLog(@"其地址:@%p",[类名 class]);

私有属性:在implementation{}中声明【这样Xcode不会提示】

私有方法:在类中只实现,不声明

类与方法在代码段中的存储方式与调用机制

Class对象与SEL对象定义过程中便已经是指针了。

(1)类以 Class对象的形式存储在代码段中,包括类名、方法、属性、isa指针(继承于NSObject【包含了NS类中所有的基本属性和方法】)。

当对象的实例方法被调用,是通过isa来找到对应的类,然后在该类的class_data_bits_t中去查找方法。class_data_bits_t是指向了类对象的数据区域。在该数据区域内查找相应方法的对应实现。

(2)方法存储于SEL对象【selector 选择器】中,再将该SEL对象作为类对象的属性

1
2
3
4
5
6
7
8
9
10
11
SEL s1 = @selector(方法名);
NSLog(@" s1 = %p", s1);//输出

[类名 方法名]
内部为:得到存储方法的SEL消息,发送给类对象,根据对象的isa指针找到存储类的类对象,在内部从查找有关SEL数据匹配的,有就执行;若没有在找父类内部直至NSObject. 均无则报错

等价于
SEL s1 = @selector(方法名);//若方法带参数,要加:
[p1 performSelector:s1]; //不带参数

[p1 performSelector:s1 withObject:参数值]; //带参数(至多两个,多于2个用构造参数类或参数结构体)

调用的本质是利用OC的消息机制,即给类对象发送SEL消息。

点语法

对象.去掉下划线的属性 本质是转换为 调用对应属性的getter/setter方法

即[对象 set/get去掉下划线的属性名首字母大写(:数据)]

这就是命名规范的重要性【属性前面下划线,set/get去掉下划线的属性名首字母大写】。

当不存在set/get函数或set/get函数命名有误,则不能使用点语法

NSLog与printf的区别

printf(暴力调试)

NSlog(检测当前程序,添加了时间戳,工程名字,进程号,线程号,自动换行)

1
2
NSLog(@"%@",CLASSNAME.obj)
// description 快速打印类中所有成员,属于NSObject类,不能显式调用

NSObject和id的异同

相同点:万能指针都可以执行任意的oc对象,
不同点:通过NSObject指针去调用对象的方法的时候,编译器会做编译检查,
通过id类型的指针去调用对象的方法的时候,编译器直接通过,无论你调用什么方法.
注意:1.指针只能调用对象的方法不能使用点语法,如果使用点语法就会直接报编译错误·
2.如果我们要声明1个万能指针千万不要使用NS0 bject而是用id

id和instancetype的区别(泛型类型)

i),instancetype只能作为方法的返回值.不能在别的地方使用;
id既可以声明指针变量也可以作为参数也可以作为返回值,
2).instancetype是有类型的,代表当前类的对象;
id是1个无类型的指针仅仅是1个地址,没有类型的指针。

方法 若内部在创建当前类的对象,用[self new]。若返回当前类的对象,用instance。

@class(类前置声明)与#import的区别

当两个类相互包含的时候【当Person.h中包含Book.h而Book.h中又包含Person.h 】,就会出现循环引用的问题,就会造成无限递归的问题,而导致无法编译通过.

解决方案:
(1)其中一边不要使用#import引入对方的头文件。使用@class 类名;来标注这是1个类,这样子就可以在不引入对方头文件的情况下,告诉编译器这是1个类。
(2)在.m文件中再#import对方的头文件,就可以使用了。

区别 :
1)#import是将指定的文件的内容拷贝到写指令的方。
2)@class 并不会拷贝任何内容,只是告诉编译器,这是1个类,这样编译器在编译的时候才可以知道这是1个类。

OC支持参数个数不同的函数重载

如果参数个数相同,无论参数和返回值类型相同与否都无法编译通过。

struct与class的区别

(1)struct无继承

(2)struct 是值类型【写时复制】,class是引用类型

访问权限

OC-010.OC类中的修饰可见性的关键字@private、@protected、@public

@private:私有的,只有本类的对象方法中中可以直接访问访问它,成员变量在那个类中定义的才能在那个类中使用
@protected:受保护的:只能在本类和子类中可以直接访问,不再外部访问。(一般默认是protected【C++为private】)

@public:公共的,大家都可以用,在本类,子类,外部都可以访问

@package: 主要用在框架内部,在框架内部相当于@protected,在框架外部相当于@private,简单理解就是在本框架内可使用,外部不行。

控制可见性的修饰符,从使用开始直到所有成员变量结束或遇到其他的控制可见性修饰符为止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#import <Foundation/Foundation.h>
@interface LSPerson : NSObject
{
//控制可见性的修饰符,从使用开始直到所有成员变量结束或遇到其他的控制可见性修饰符为止

@private //私有的,只有本类的对象方法中中可以直接访问访问它
int _age;
@protected //受保护的:只能在本类和子类中可以直接访问,不再外部访问
NSString *_name;
@public //公共的,大家都可以用,在本类,子类,外部都可以访问
double _height;
@package //主要用在框架内部,在框架内部相当于@protected,在框架外部相当于@private
double _weight;

}

@end

属性

合成存取器【属性是为成员变量服务的】

1
2
3
4
5
6
7
8
9
10
11
12
13
@property 属性名(去掉_的成员变量名)
作用:自动生成类属性的get/setget/set方法的声明【写在类的声明interface大括号外,end前】,而编译器在编译过程中遇到@property自动生成类属性的get/set方法的实现。
使用: @property 数据类型 成员变量名
(1)点运算访问成员变量 本质是调用了set/get方法
(2)可以直接由属性声明成员变量(会自动生成 _属性名 的成员变量)
(3)synthesize 自定义 【自动生成的成员变量名】
eg: synthesize name = _myName;

@property【自动生成属性getter/setter方法的声明,写在interface大括号外,end前】
@synthesize 【自动生成属性getter/setter方法的实现,写在implementation中】自动生成真私有的(即不被XCode提示)属性(不带下划线的)以及getter/setter方法(均是处理所自动生成的属性值)
@synthesize 属性名 = 已声明(带下划线的)属性名 【指定操作】
@property在XCode4.4加强,可以自动生成(带下划线的)私有属性以及对应属性getter/setter方法的声明与实现。
PS: 同时重写 getter、setter方法 、@property则不会自动生成私有属性。

常用修饰词

1
@property(修饰词1,修饰词2,...)数据类型 名称;

属性类型相关

assign 默认,一般用于非OC对象类型的属性 (生成的setter方法的实现:直接赋值)

OC对象类型

【MRC模式】retain 用于OC对象类型的属性(生成的setter方法的实现:为标准MRC内存管理代码,需要手动在本类的dealloc中释放
【ARC模式】strong/weak (强弱指针涉及到对象的引用计数)

copy 多用于NSString类型的属性【深拷贝】

线程相关

nonatomic 默认,适用单线程
atomic 多线程程序(setter方法的代码会加上一把线程安全锁)

访问权限相关

readonly 只生成getter方法
readwrite 生成getter/setter方法

方法生成相关

setter 自定义生成的setter方法名(带参要加:)
getter 自定义生成的getter方法名

1
2
3
4
5
6
@property(nonatomic,assign,getter=isMan,setter=XYZ:)BOOL sex;
//对应getter/setter方法
-isMan();
-XYZ:(BOOL sex);

在使用点语法,编译器直接调用 自定义的getter/setter方法名

类的扩展

分类

将一个类分为多个模块,或者为已经存在的类添加方法

1
2
3
4
5
@interface 类名(分类名)
@end

@implementation 类名(分类名)
@end

注意:

1.分类只能增加方法,不能增加属性

2.在分类之中可以写@property, 但是不会自动生成私有属性,也不会自动生成getter setter的实现。
只会生成getter setter的声明。 所以,需要在本类中定义属性且 分类中完成getter 和setter的声明。

3.在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)但是可以调用本类的getter setter来访问属性。
即本类的@property生成的私有属性,只可以在本类的实现中访问。分类中不能直接访问真私有属性,但可以使用 getter setter 来访问。

4.当分类中有和本类中同名的方法的时候,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后编译的分类。【TARGETS->Build Phases -> Compile Sources 查看文件编译顺序】

延展

是一个特殊的分类,没有名字,没有实现。
只有声明,和本类共享一个实现。

1
2
3
4
@interface 类名()
//属性
//方法声明,在本类中实现
@end

主要用途:(真)私有化类的成员

eg:要为类写1个私有的@property.且生成getter、setter方法只能在类的内部访问 不能在外部访问。
解决方案:延展100%的情况下不会独占1个文件,都是将延展直接写在本类的实现文件(常见于ios UI 私有控件)中。这个时候,写在延展中的成员,就相当于是这个类的私有成员。只能在本类的实现中访问。外部不能访问。

真私有属性

如果想要为类写1个真私有属性,虽然我们可以定义在@implementation之中,但是不要这么写 这样很不规范。写1个延展。将这个私有属性声明在延展中

如果要为类写1个私有方法,建议将声明写在延展中,实现写在本类的实现中,提供代码的阅读性如果想要为类写1个私有的@property 就直接写在延展就可以了,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
实现方式一:【不推荐】
@interface 类名{
@private
//属性... 【xcode仍能够有提示,可以调用get/set方法间隔访问】
}
@end

@implementation 类名{
//属性...【真私有属性,xcode都不会提示,同时get/set方法只能在类的内部访问,不能在外部访问】
*** XXX;
}
-(void)setXXX:(***)XXX
{
_XXX = XXX;
}
-(***)getXXX
{
return XXX;
}
@end

实现方式二:
在本类的.m文件中,使用延展
#import "类名.h"
@interface 类名()
@property (nonatomic, ...) ***XXX;
@end
@implementation 类名

@end

延展和分类的区别

1.分类有名字,延展没有名字 是1个匿名的分类。

2.每1个分类都有单独的声明和实现,而延展只有声明 没有单独的实现 和本类共享1个实现,

3.分类中只能新增方法,而延展中任意的成员(属性、方法)都可以写.

4.分类中可以写@property 但是只会生成getter setter的声明。

延展中写@property 会自动生成私有属性也会生成getter setter的声明和实现。

block

block 是一种存储一段代码的数据类型,类似于C++函数指针

1
2
3
4
5
6
7
8
9
10
11
返回值类型 (^block变量名)(参数列表) = ^返回值类型(参数列表){
// 代码段
}
//简写 当将blockf为函数的返回值的时候,返回值的类型就必须要使用typedef定义的短类型
typedef 返回值类型 (^block变量名)(参数列表);
eg: 定义了新类型 NewType 本质是 一个拥有两个int参数、返回int的block
typedef int (^NewType) (int num1,int num2);
NewType t1 = ^int(int num1, int num2){
int num3 = num1 + num2;
return num3;
};

block简写规则

代码段简写

1.如果代码段没有返回值。那么代码段的void可以省略,

2.如果我们写的代码段没有参数,那么代码段的小括弧写可以省路。

1
2
3
4
5
//综上两条
int (^myBlock2)() = ^{
intnum1=10+20;
return num1 ;
{

3.无论代码段是否有返回值,在写代码的时候省略返回值类型。
这个时候系统会自动的确定返回值的类型【代码段中没有返回任何数据为void,有返回数据则为对应类型】。

声明block变量简写

4.声明block变量的时候,如果有指定参数,可以只写参数的类型而不写参数的名称;

1
2
3
4
5
//综上两条
int (^myBlock3)(int,int) = ^(int num1, int num2){
int num3= numl + num2;
return num3;//自动确认返回int型
};

block访问外部变量

1.在block代码块的内部可以读取定义在外部的变量的值【包括全局变量和在外部的局部变量】。
2.在block代码块的内部可以修改全局变量的值,但是不能修改在外部的局部变量的值【若要修改,该变量需添加_ _block的修饰符】。

block与函数的区别

相网点:都是封装1段代码,
不同点:

  1. block是1个数据类型。 函数是1个函数。
  2. 我们可以声明black类型的安量函数就只是函数。
  3. block可以作为函数的参数。而函数不能直接作为函数的参数.

协议protocol

专门用来声明一大堆方法【.h并且只有1个.h文件.】。(不能声明属性,也不能实现方法,只能用来写方法的声明).只要某个类遵守了这个协议.就相当于拥有这个协议中的所有的方法声明。相当于C++中的纯虚函数

类是单继承,可以多协议

1
2
3
4
5
6
7
8
9
10
11
@protocol协议名称<NS0bject>
@required
//方法的声明;【遵守这个协议的类必须要实现这个方法,否则编译器会发出警告.】
@optional
// 方法的声明; 【遵守这个协议的类如果不实现这个方法.编译器也不会报警告.】
@end


//当1个类遵守了多个协议之后,就相当于这个类拥有了所有协议中定义的方法的声明。那么这个类,就应该实现所有协议中的方法.当创建对象,来调用这个没有实现的协议中的方法的时候就会报错。
@interface类名:父类名<协议 名称1,协议名称......>
@end

协议与协议之间可以相互继承

NSObject协议被NS0bject类遵守(类的名称可以和协议的名称一致)

要求所有的协议都必须直接的或者间接的从NS0bject基协议继承。

1
2
3
4
//子协议中不仅有自己的方法的声明,还有父协议中的所有的方法的声明.如果1个类遵守了某份协议,那么这个类就拥有这个协议和这个协议的父协议中的所有的方法声明。
@protocol协议名称< 父协议名称>
// 方法的声明
@end

协议类型限制指针

声明1个指针变量,要求这个指针变量指向的对象必须遵守多个协议。

1
2
3
// obj1是遵守StudyProtocol, SBProtocol协议的指针,其指向的对象也必须遵守StudyProtocol, SBProtocol协议。
NSObject<StudyProtocol, SBProtocol> *obj1 = [Student new];
id<StudyProtocol, SBProtocol> obj1 = [Student new];

2).效果:
子协议中不仅有自己的方法的声明,还有父协议中的所有的方法的声明.
如果1个类遵守了某份协议,那么这个类就拥有这个协议和这个协议的父协议中的所有的方法声明。
2.介绍1个东西. NSObject
在Foundation框架中,有1个类叫做NSObject 是所有0C类的基类。
在Foundation框架中,有1个协议。叫做NSObject.
NSObject协议被NS0bject类遵守.所以,NSObject协议中的所有的方法全部的0C类都拥有了.
这么说,所有的0C类都遵守了NSObject协议。NSObject协议叫做基协议。
类的名称可以和协议的名称-致。
3.写协议的规范:
要求所有的协议都必须直接的或者间接的从NS0bject基协议继承。

使用

由协议(@Protocol)、代理对象(id delegate)和委托者()三部分组成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//声明协议
@protocol TestDelegate <NSObject, OtherDelegate> //协议可多继承

@required //必须实现的方法(默认)
@option //可选实现的方法
- (void) test; //定义一个方法

@end


//代理对象,使用weak修饰,防止委托者被受委托者持有造成循环引用、内存泄露。
@property (nonatomic, weak) id <TestDelegate> delegate;


//委托者
@interface YwClass ()<TestDelegate> //服从协议

YwClass().delegate = self; //指定代理对象
- (void) test {} //实现协议中的方法

@end

检查

编译检查:在编译时,能不能调用对象的方法主要看指针所属类型。(看左边)
运行检查:在运行时,能不能调用对象的方法主要看指针所指向对象类型。(看右边)

C++的多态性内部实现—迟绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1).判断对象中是否有这个方法可以执行.【实例方法】
- (BOOL) respondsToSelector:(SEL)aSelector;

2).判断类中是否有指定的类方法。【类方法】
+(BOOL)instancesRespondToSelector:(SEL)aSelector;

3).判断指定的对象是否为 指定类的对象或者子类对象。
- (BOOL)isKind0fClass: (Class)aClass;
BOOL b1= [s1 isKindOfClass:[Person class]]; 判断s1对象是否为Person对象或者Person的子类对象。

4).判断对象是否为指定类的对象 不包括子类.
- (BOOL) isMember0fClass: (Class)aClass;
[s1 isMemberofClass:[Student class]];
判断s1对象是否为1个Student对象,不包括Student的子类对象.

5).判断类是否为另外1个类的子类。
+(BOOL)isSubclass0fClass:(Class)aClass;

内存管理

只需要管理存储在堆上的OC对象的生成与释放(本质是所占用内存的权限变更)

引用计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1).为对象发送1条retain消息。对象的引用计数器就会加1.当多1个人使用对象的时候才发。
2).为对象发送1条release消息.对象的引用计数器就会减1.当少1个人使用对象的时候才发。
3).为对象发送1条retainCount消息。就可以去到对象的引用计数器的值。
4)当对象的引用计数器变为0的时候,对象就会被系统立即回收。在对象被回收的时候。会自动调用对象的dealloc方法.【在MRC模式下,可以重写dealloc方法,在最后需要调用[super dealloc]】

注意事项:
在MRC模式下,setter传递OC对象【注意NSString类】时应注意内存泄漏:
(1) setter传入对象时 旧对象的泄露
(void)setCar:(Car *)car {
//判断是否为同一对象
if(_car != car){
// 释放旧对象, 旧对象的引用计数--;
[_car release];
// 接收新对象, 新对象的引用计数++;
car = [car retain];
}
}

或者 @property(nonatomic, retain) Car *car;


// 本类资源释放
-(void) dealloc{
[_car release];
[super dealloc];
}

(2)循环引用
解决方案
【MRC模式】
1端使用使用 retain, 另一端使用assign,且dealloc中不需要release
【ARC模式】
1端使用使用 strong, 另一端使用weak

僵尸对象

1个已被释放的对象,但其空间还未分配给别人,无法复活僵尸对象(这脑洞笑死)。【打开僵尸对象实时检查机制】

野指针

C语言中的野指针:定义1个指针变量,没有初始化,这个指针变量的值是1个垃圾值,指向1块随机的空间。

OC中的野指针:指针指向的对象已经被回收了

解决:【设为nil,但调用对象的方法(包括点语法)时Xcode不会报错,只是没有任何反应;->访问属性会报错】

衍生出:objc中向一个nil对象发送消息将会发生什么?

首先了解消息发送机制以及isa指针的作用。

isa指针是用于对象指向类对象,类对象指向元类对象的一个指针。而类对象和元类对象中又分别存放对象方法和类方法。 在消息传递机制中,就是通过isa指针来寻找到方法的实际调用地址的。

objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后在发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。

如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误。给nil发送消息直接return,所以不会发生崩溃。
null是NSNull的实例,给这个对象发送一个它无法响应的消息自然会发生崩溃,报错:unrecognized selector

autoreleasepool (自动释放池)

作用:清理动态分配对象【由工厂模式创建的、自动释放池对象】

自动释放池的原理:存入到自动释放池中的对象【不会使得对象的引用计数器++】,在自动释放池被销毁的时候自动调用存储在该自动释放池中的所有对象的release方法【不一定会直接销毁对象】。

1
2
3
4
@autoreleasepool {
Person *p1=[[[Person alloc] init] autorelease];
[p2 autorelease]; //autorelease 多次,也会发送多条release消息 【所以同一对象只autorelease1次】
}//代表这个自动释放池的范围

ARC机制

当ARC开启时,编译器会自动的在合适的地方播入retain、release、autorelase代码。

本质:对象的引用计数器为0的时候,自动释放。

表象:只要没有强指针指向这个对象【即指向该对象的所有强指针被回收或赋值为nil】,这个对象就会立即回收。此时,指向该对象的所有弱指针均被赋值为nil

强指针与弱指针

强指针:_strong标识或者默认情况的指针

弱指针:_weak标识的指针

无论是强指针还是弱指针,都是指针,都可以用来存储地址,都可以通过这个指针访问对象的成员。

唯一的区别就是在ARC模式下,他们用来作为回收对象的基准。

@property 禁用retain

ARC机制不需要,对于OC对象属性会自动生成符合MRC标准的内存管理代码

禁用

(1)不允许调用retain、release、retainCount、autorelease 方法

(2)在dealloc中不允许[super dealloc]

ARC机制 与 垃圾回收机制GC的区别

GC 在运行时通过各种策略扫描堆中没有在使用的对象并回收。

ARC 在编译时插入retain、release、autorelase代码,可以满足对象引用计数为0时便释放。

【待补充】动态特性

动态类型

动态绑定

动态加载

数据结构

NS_OPTIONS、NS_ENUM和enum

NS_ENUM就是对enum的一种封装。所以,在OC中,几乎不使用enum,都是使用NS_ENUM和NS_OPTION。

https://www.jianshu.com/p/994dc9f4958d

enum不能直接在使用typedef的同时,确定枚举的底层数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef enum {
FlyStateOne,
FlyStateTwo,
FlyStateThree
}FlyState;

enum FlyState:NSInteger{ //设置底层数据类型为NSInteger
FlyStateOne,
FlyStateTwo,
FlyStateThree
};

//不可以这样【在使用typedef的同时,确定枚举的底层数据类型】
typedef enum FlyState1:NSInteger{
FlyStateOne1,
FlyStateTwo1,
FlyStateThree1
};

NS_ENUM是一个OC中的宏,可以判断编译器能否采用新式枚举:如果不能,那么效果等同于仅仅使用typedef的enum;如果能够采用新式枚举,那么NS_ENUM所定义的枚举类型,就是处理后的enum类型,可以在使用typedef的同时,指定底层数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
//类型【只能是基础类型】,枚举变量名
typedef NS_ENUM(NSInteger, FlyState) {
FlyStateOne,
FlyStateTwo,
FlyStateThree
};
展开后:
typedef enum FlyState:NSInteger FlyState;
enum FlyState : NSInteger {
FlyStateOne,
FlyStateTwo,
FlyStateThree
};

按位或 枚举的作用

NS_OPTIONS也是OC中的一个宏,在非C++ 编译模式下,NS_OPTIONS和NS_ENUM是一样的。对于C++模式编译,NS_OPTIONS会做一些特殊处理,保证枚举结果的类型正确。
在用或运算处理两个枚举类型时,C++ 认为枚举结果的数据类型应该是枚举的底层数据类型(即NSUInteger),而且C++不允许将这个底层类型隐式转换成枚举类型本身。

1
2
3
4
5
6
7
8
9
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

NSString

NSString 不可变字符串,即当在内存中创建1个字符串对象以后.这个字符串对象的内容就无法更改【无论在常量区还是堆区】。

1
2
3
NSString *str = @"jack"; //0C字符串常量存储在常量区(数据段)

NSString *str =[NSString stringWithFormar:@" jack"];//调用NSString的类方法来创建对象的时候。创建的字符串对象是存储在堆区。

注意点

1.当我们重新为字符串指针初始化值的时候.并不是修改原来的字符串对象而是重新的创建1个字符串对象。将这个字符串对象的地址重新复制给字符串指针变量。
2.当系统准备要在内存中创建字符串对象的时候。会先检查内存中是否有相同内容的字符串对象。如果有,直接指向。如果没有才会重新创建。
3.存储在常量区的数据不会被回收,所以存储在常量区的字符串对象也不会被回收。
4.stringbyappend后是申请了新内存,所以需要别的指针指向这一块内存。
NSMutableString appendString, 则是可变的
PS: OC中一个汉语字符长度为1,C中一个汉字两个字节【取决于编译器】用 unichar (unsigned short)来存储汉字字符

深拷贝与浅拷贝

1.无论在MRC还是ARC下 ,如果属性的类型是NSString类型的。@property参数使用copy.

2.拷贝
1).copy方法是定义在NSObject类中的1个方法。copy方法的内部调用了另外1个方法copyWithZone:
这个方法是定义在NSCoping协议之中的。【作用:拷贝对象。】
NSString —> copy —> 不可变字符串没有产生新对象 ,而是直接将对象本身的地址返回,浅拷贝。
NSMutableString –> copy –>是1个不可变的字符串对象,产生1个新对象,深拷贝
2). mutableCopy.定义在NSObject类之中。作用:拷贝对象【深拷贝】。
NSString—> mutableCopy –>可变字符串对象,深拷贝.
NSMutabieString –> mutableCopy –>可变字符串对象,深拷贝。

3.字符串对象拷贝的引用计数器的问题。
1).若字符串对象存储在常量区中,存储在常量区的数据是不允许被回收的。
所以存储在常量区的字符串对象的引用计数器是1个超大的数,并且retain和release无效。
2).若字符串存储在堆区.这个字符串对象和普通的对象一样的.引用计数器默认是1.
3).字符串对象如果是浅拷贝,会将对象的引用计数器+1。字符串对象如果是深拷贝。原来的对象的引用计数器不变,新拷贝出来的对象的引用计数器为1.

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1.使用拼接的方式创建1个NSString对象.
+(instancetype)stringWithFormat: (NSString *)format, ...

2.得到字符串的长度。
@property (readonly) NSUInteger length;

3.得到字符串中指定下标的字符。
- (unichar) characterAtIndex: (NSUInteper) index;
返回值是unichar类型的要打印的话使用%C

4.判断两个字符串的内容是否相同.
a.不能使用==来判断两个0C字符串的内容是否相同.因为==比较的是字符串指针变量的值.而我们要比的是两个字符串指针指向的字符串对象的内容是否相同。
- (BOOL) isEqualToString: (NSString *)aString;
就可以比较当前字符串对象和传入的字符串对象的内容是否相同.
//==运算符的作用:比较左右两边的数据是否相同.
10 == 10这个时候 直接比较的是左右两边的数据是否相同.
a == b两边也可以写1个变量. 这个时候比较是左右两边的变量的值是否相同.
如果两边是1个指针变量.则比较的是指针变量的值是地址。

5. 将C语言的字符串转换为0C字符串对象。
+ (instancetype)stringWithString (NSString *)string;

6.将0C字符串对象转换为C语言的字符串.
@property (nullable, readonly)_ _strong const char *UTF8String

7.从磁盘上的文件中读取文件中的内容。
+ (instancetype) stringWithContentsOfFile: (NSString *)path encoding:(NSSt ringEncodingienc error: (NSError **)error;

8.将字符串内存写入到磁盘上的某1个文件之中。
- (BOOL)writeToFile: (NSString *)path atomically:(BOOL)useAuxiliaryFile encoding:
(NSStringEncoding)enc error:(NSError **)error;
参数1:将字符串内容写入到那1个文件之中写上文件的路径,
参数2: YES先将内容写入到1个临时文件.如果成功再将这个文件搬到指定的目录。安全,效率较低。NO,直接将内容写入到指定的文件不安全,效率高【推荐使用NO】。
参数3:指定写人的时候使用的编码。一般情况下这里写.NSUTF8StringEncoding使用UTF-8编码。
参数4:二级指针,要传递1:个NSError指针的地址。
如果写入成功这个指针的值就是nil
如果写入失败,这个指针就会指向1个错误对象.这个对象描述了发生错误的信息。
这个对象的localizedDescription方法可以得到发生错误的简要信息。
所以我们要判断是否写入成功,也可以判断这个指针的值是否为nil
如果不想知道发生错误的原因,那么第4个参数给nil就可以了.

9.使用NSURL读取字符串
>将本地文件路径封装在NSURL对象之中,
NSURL *url = (NSURL URLWithString:@"file://Users/Highven/Desktop/123.txt";
>将网页地址路径封装在NSURL对象之中.
>NSURL *url = (NSURL URIWithString:@"http://zitcast.cn";
>將ftp文件路径封装在NSURL对象之中.
>NSURL *url = [NSURL URLWithSring:@"ftp://server.itcast.cn/io/cjk.txt":
>4).读取NSURL对象中保持的路径的文件数据.

+ (instancetype)stringwithContentsOfURl : (NSURL *)url encoding: (NSStringEncoding)enc error: (NSError **)
error;
第1个参数:封装了地址的NSURL对象.
第2个参数:编码方式.一般传递NSUTF8StringEncoding
第3个参数:二级指针.传入1个NSError的1个二级指针.如果方法执行成功则方法执行完毕之后,这个NSError的指针的值为nil

NSMutableString是NSString的子类.

NSMutableString从NSString继承.存储在NSSMutableString对象中的字符串数据可以更改【不会新创建对象】.具备可变性,
NSMutbaleStrint: 做大批量的字符串拼接【10次以上】的时候才使用。

1
2
3
4
5
6
7
8
9
10
NSMutableString *str = (NSMutableString string];
//创建NSMutableString对象的时候, 记住下面这样的初始化方式是不行的。
NSMutableString *str = @"jack";
@"jack"是1个NSString对象, 是1个父类对象。
而str指针是1个NSMutabiestring类型的是1个子类类型的。
如果通过子类指针去调用子类独有的成员就会运行错误。

//往可变字符串对象中追加字符串。
- (void)appendString: SString *)aString; 直接追加内容.
- (void) appendFormat: (NSString *)format, ...以拼接的方式往可变字符串对象中追加内容.

NSMutableAttributedString

集合

NSArray、NSDictionary字典就叫做集合.

在MRC的模式下将1个对象存储到集合中
(1)会影响对象的引用计数器.
将对象存储到集合之中,会为这个对象的引用计数器+1
当集合销毁的时候就会像存储在集合中的所有的对象发送1条release消息。
(2)使用@[]或者@{}创建的集合已经是被autorelease过的了.
直接调用和类同名的类方 法创建的对象也是被autorelease过的了.

在ARC的模式下,集合的元素是1个强类型的指针。

NSArray

1).是Foundation框架中的一 一个类.这个类的对象是来存储多个数据的,具备数组的功能。
所以,NSArray是OC中的数组.
2).特点:
a.只能存储OC对象.任意的OC对象无法存储
b.长度固定。一旦NSArray数组创建完毕之 后,元素的长度固定,无法新增无法删除元素【nil为元素终止标识符】。

1
2
3
NSArray *arr = [NSArray arrayWithobjects:@"jack" ,nil,@"rose" ,@"lili",nil];
// 简单模式: NSArray *arr = @[写上每1个元素的值用逗号分隔.]; 注意:这种创建方式 不需要在最后加nil
NSArray *arr = @[@"jack" ,@"rose",@"lili"];

c.每1个元素都是紧密相连的。每1个元素仍然有自己的下标。
d.元素的类型是id类型的。

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//取值
// 可以直接 [index], 也可以使用如下方法
- (ObjectType)objectAtIndex: (NSUInteger)index;
NSArray *arr = @[@"jack" ,@" rose" ,@"lili"];
NSString *str = [arr objectAtIndex:3];
NSLog(@"*@",str);

//遍历
1.index 遍历
2.for in 来遍历【推荐使用id类型,防止NSArray内部类型不一致】
3.使用block遍历。
- (void) enumerate0bjectsUsingBlock: (void (^) (0objectType obj, NSUInteger idx, BOOL
*stop))block

1.得到NSArray数组中的元素的个数。
@property (readonly) NSUInteger count;
2.判断NSArray数组中是否包含指定的元素。
- (BOOL) containsObject: (ObjectType) an0bject;
3.取到NSArray数组中的第1个元素.
@property (nullable, nonatomic, readonly) ObjectType first0bject
//与arr[0]的区别:如果数组中没有任何元素,arr[0]报错。firstObject取到nil 不报错。
4.取到NSArray数组中的最后1个元素.
@property (nullable, nonatomic, readonly) ObjectType lastobject
5.查找指定的元素在NSArray数组中第一次出现的下标。
- (NSUInteger) indexofobject: (ObjectType)anObject;
如果没有找到返回的是NSUInteger的最大值.

6.将数组中的元素连接起来组成1个新的字符串.
- (NSString *) componentsJoinedByString : (NSString *) separator
参数:连接符.

7.将字符串以指定的分隔符分成1个数组。每1部分就是 数组的1个元素.
- (NSArray<NSString *> *)componentsSeparatedByString: (NSString F*)separator;

NSMutableArray是NSArray的子类.

  1. NSMubleArray仍然是1个数组.具备NSArray数组的特点。
    只能存储OC对象.每1个元素的紧密相连的。
  2. NSMutableArray相对于父类做的扩 展:NSMutableArray数组的元素可以动态的新增和删掉
    其他的用法均与NSArray-一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSMutableArray *arr1 = [NSMutableArray new];
NSMutableArray *arr2 = [[NSMutableArray alloc] init];
NSMutableArray *arr3 = [NSMutableArray array];
这样创建出来的数组对象.数组的元素是0仍然是有意义的因为我们可以动态的新增和删除元素。
也可以使用这样的方式来创建可变数组对象.这个数组的元素可以新增和删除.
NSMutableArray *arr4 = [NSMutableArray arrayWithobjects:@"jack" ,@"rose" ,@"1ili", nil];

//最容易犯错:这样写是不可以的
NSMutableArray *arr5 = @[@"jack" ,@"rose" ,@"1lil"];
//@[@"jack" ,@" rose" ,@"lili"];这是1个NSArray对象.arr5是1个子类指针。子类指针指向父类对象的就有可能会出问题。

1.如何往可变数组中新增元素
- (void)addobject: (objectType) an0bjectt将传入的参数作为数组的元素添加进去。//注意插入一个NSArray对象时,是将整个数组看作一个整体插入,NSMutableArray的count+1
2.将另外1个数组中的每1个元素添加到可变数组中。
- (void)addobjectsFromArray: (NSArray<0bjectType> *)otherArray;//注意插入一个NSArray对象时,NSMutableArray的count+NSArray对象.count
3.在可变数组中指定的“下标出插入1个元素.
- (void) insert0bject: (0bject Type)an0bject atIndex: (NSUInteger) index;

NSMutableArray的删除注意事项

来源:iOS小知识(2016.07)—NSMutableArray中删除对象知多少

removeObjectAtIndex:删除指定NSMutableArray中指定index的对象,注意index不能越界。【最多只能删除一个对象】

removeObject:删除NSMutableArray中所有isEqual:待删对象的对象可以删除多个对象(只要符合isEqual:的都删除掉)

不要在for in 循环中删除数组内部对象,“易引发崩溃” 大致于C++迭代器失效类似原因。

解决方案:

(1)倒序删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)removeObjectsReversed
{
// 可以借鉴这种初始化方法
NSMutableArray *contents = [@[@"how", @"to", @"remove", @"remove", @"object"] mutableCopy];
for (NSInteger i = contents.count - 1; i >= 0; --i) {
NSString *var = contents[i];
if ([var isEqualToString:@"remove"]) {
[contents removeObjectAtIndex:i];
}
}

NSLog(@"%@", contents);
}

//结果:
2016-07-31 21:31:37.655 RemoveObject[5934:325316] (
how,
to,
object
)

(2)先通过indexesOfObjectsPassingTest:把待删除对象的index找出来,再调用removeObjectsAtIndexes:进行一次性删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)removeObjectsUseEnumration
{
NSMutableArray *contents = [@[@"how", @"remove", @"to", @"remove", @"object"] mutableCopy];
NSIndexSet *indexSet =
[contents indexesOfObjectsPassingTest:^BOOL(NSString * _Nonnull var, NSUInteger idx, BOOL * _Nonnull stop) {
return [var isEqualToString:@"remove"];
}];
[contents removeObjectsAtIndexes:indexSet];

NSLog(@"%@", indexSet);
NSLog(@"%@", contents);
}

//结果:
2016-07-31 22:10:42.404 RemoveObject[6014:338210] <NSIndexSet: 0x7fb73a516040>[number of indexes: 2 (in 2 ranges), indexes: (1 3)]
2016-07-31 22:10:42.404 RemoveObject[6014:338210] (
how,
to,
object
)

NSDictionary

1).这个数组是以键值对的形式存储数据的每往这个数组中存储1个数据,就要求为存储的数据取1个别名.
2).别名必须是遵守了NSCopying协议的对象。NSString就是遵守 了这个协议的。存储的数据只能是任意的0C对象
3).长度固定.NSDictionary数组一旦创建 ,其长度就固定,键值对无法新增和删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
//NSDictionary数组的创建 .
NSDictionary *arr1 = [NSDictionary new] ;
NSDictionary *arr2 = [ [NSDictionary alloc] init];
NSDictionary *arr3 = [NSDictionary dictionary];
创建的数组的元素的个数是0没意义。

比较常用的一种方法.创建NSDictionary数组的同时指定键值对。//写上值再写键.

- (instancetype)dictionaryWithobjectsAndKeys:(id)first0bject, ...
NSDictionary *dict = [NSDictionary dictionaryWithobjectsAndKeys:@"jack" ,@"name" ,@"17" , @"age" ,@"175.5" ,@"height", nil];
简要创建的方式。
NSDictionary *dict = @{键1:值1,键2:值2,键3:值.......}; .
NSDictionary *dict =@{@"name" :@"jack" ," age" :@"17" ,@"height" :@"175.5"};

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//取值【往字典数组中存储键值对的时候键不允许重复.】
字典数组名[键];这样就可以去到字典数组中这个键对应的值.
NSLog(@"%@" , dict [@"name"]);取出dict字典数组中@"name"这个键对应的值.
->调用字典数组对象的方法也可以取出键对应的值.
- (nullable ObjectType)objectForKey: (KeyType)aKey;
如果给定的key在数组中不存在取到的值是nil不会报错.
取到字典数组的键值对的个数.
@property (readonly) NSUInteger count;


//遍历
1.使用for in循环。遍历出来的是字典数组中所有的键。再通过键取出对应的值.
NSDictionary *dict = @{
@"name":@"rose",
@"age":@"18"
@"address":@"BeiJingXXSttreet"
};
for(id item in dict){
NSLog(@"%@ = %@" , item, dict [item]);
}

2.使用block遍历.
[dict enumerateKeysAndobjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *
_Nonnull stop) {
NSLog(@"%@ = %&@" , key, obj);
}];

//持久化
1.将字典数组的信息保存到plist文件中.
- (BOOL)writeToFile: (NSString *)path atomically: (BOOL)useAuxiliaryFile;

2.从plist文件中还原回字典.
+ (nullable NSDictionary<KeyType, 0bjectType> *)dictionaryWithContentsOfFile: (NSString *)path;

【待补充】引出nonnull / nullable、_Nonnull / _Nullable、nonnull / nullable区别 &&nil、Nil、NULL、NSNull区别 - 简书 (jianshu.com)

block遍历中出现_Nonnull

存储原理

并不是按照顺序挨个挨个的存储的。存储键值对的时候,会根据键和数组的长度做1个哈希算法.算出1个下标。将这个键值对存储在该下标处.【NSArray存储效率更高】
取值的时候:也是根据键做1个哈希算法.就可以算出这个键值对存储的下标然后直接找到这个下标的数据取出.

NSMutableDictionary是NSDictionary的子类 .

所以NSMutableDictionary也是1个字典数组,也是以键值对的形式存储数据的.存储在其中的元素可以动态的新增和删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1.创建可变字典数组。
NSMutableDictionary *dict1 = [NSMutableDictionary new] ;
NSMutableDictionary *dict2 = [ [NSMutableDictionary alloc] init];
NSMutableDictionary *dict3 = [NSMutableDictionary dictionary];
这样创建出来的可变字典数组的长度为0但是有意义因为可以动态的新增和删除.
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithobjectsAndKeys:@"jack" ,@"name" ,@"18" ,@"age", nil];
在创建可变字典数组的同时初始化键值对.
//注意: NSMutableDictionary *dict = @{};这样是不行的。【原因:指向父类对象】

2.如何新增键值对。
- (void)set0bject: (ObjectType)an0bject forKey: (KeyType <NSCopying> )aKey;
如果键重复.后添加的就会替换原有的.

3.如何删除。
- (void) removeAllobjects; 删除所有的键值对。
(void) remove0bjectForKey: (KeyType)aKey;删除指定的键值对。

NSFileManger

NSFileManger用来操作磁盘上的文件文件央对他们进行创建、 删除、复制拷贝移动…..
这个类的对象是以单例模式创建的.

1
2
//得到NSFileManager的1个单例对象。
NSFileManager *fileManager = [NSFileManager defaultManager] ;

单例模式

实现单例模式【1个类的对象,无论在何时创建也无论在什么地方创建也无论创建多少次.创建出来的都是同1个对象.

重写+ allocWithZone:
无论如何创建对象,最终都会调用alloc方法来创建对象。
1),alloc方法的内部。 其实什么都没有做,只是调用了allocWithZone:方法。
2),实际上真正申请空间创建对象的事情是allocWithZone:方法在做。

1
2
3
4
5
6
7
8
9
// 懒汉模式
+ (instancetype)allocwithZone: (struct. _NSZone *)zone
{
static id instance = nil;
if(instance == nil)
instance = [super allocWithZone:zone];
}
return instance :
}

单例模式的规范:如果类是1个单例模式.要求为类提供1个类方法.来返回这个单例对象。
类方法的名称必须以shared类名; default类名;

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//判断.
1.判断指定的文件或者文件夹在磁盘.上是否真实的存在
- (BOOL)fileExistsAtPath: (NSString *)path;
2.判断指定的路径是否真实的存储在我们的磁盘之上,并且判断这个路径是1个文件夹路径还是1个文件路径。
- (BOOL)fileExistsAtPath: (NSString *)path isDirectory: (B0OL *) isDirectory;
返回值:代表这个路径是否真实存在.
参数指针:代表这个路径是否是1个文件夹路径
3.判断指定的文件夹或者文件是否可以读取.
- (BOOL) isReadableFileAtPath: (NSString *)path;
4.判断指定的文件央或者文件是否可以写入.
- (BOOL) isWritableFileAtPath: (NSString *) path;
5.判断指定的文件夹或者文件是否可以删除.
- (BOOL) isDeletableFileAtPath: (NSString *) path
//获取信息.
1.获取指定文件或者文件夹的属性信息.
- (NSDictionary *)attributes0fItemAtPath: (NSString *) path error:; (NSError **)error
返回的是1个字典,如果要拿到特定的信息通过key
2.获取指定目录下的所有的文件和目录。是拿到指定目录下的所有的文件和目录所有的后代目录和文件.
- (NSArray *) subpathsAtPath: (NSString *) path;

...

CG【core graphic】

CGPoint

这个结构体一般情况下是用来表示坐标的,用来表示控件在界面上得位置.
CGPoint与NSPoint都是同1个结构体,只不过定义两个名字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//定义
typedef CGPoint NSPoint;
struct CGPoint {
CGFloat X;//CGFloat类型的实际上就是1个double类型的.
CGFloat y;
};
typedef struct CGPoint CGPoint;


//初始化
CGPoint p1;
p1.x = 20;
p1.y = 30;
2). CGPoint p1 = {20,30};
3). CGPoint p1 = {.x = 20,.y = 30};
4). Foundation框架 中提供的函数来快速的创建1个CGPoint变量.
a. CGPointMake(x,y);
CGPoint p1 = CGPointMake(20, 30);
b. NSMakePoint(x,y);
NSPoint p2 = NSMakePoint(20, 30);

CGSize

声明1个变量来保存某个控件的大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义
struct CGSize {
CGFloat width;
CGFloat height;
};
typedef struct CGSize CGSize;
typedef CGSize NSSize;
NSSize和CGSize是同1个结构体,只不过定义了两个名称。
CGSize结构体-般情况下用来表示控件的大小。

//-----CGSize声明并初始化的方式- - ------
1). CGSize size;
size.width = 100;
size.height = 30;
2). CGSize size = {100,30};
3).CGSize size = {.width = 100,.height = 30};
4).Foundation框架中提供了函数用来快速的得到1个CGSize结构体变量.
a. CGSizeMake (width,height);
CGSize size0 = CGSizeMake(100, 30);
b. NSMakeSize(w,h);
CGSize size1 = NSMakeSize(100, 30);

CGRect

这个结构体变量一般情况下存储1个控件的位置和大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//定义
struct CGRect {
CGPoint origin;
CGSize size;
};
typedef struct CGRect CGRect;
typedef CGRect NSRect;
NSRect和CGRect是一样的。

//---CGRecr的声明和初始化
1).CGRect rect;
rect.origin.x = 20;
rect.origin.y = 40;
rect. size.width = 100;
rect. size. height = 30;
//注意 当结构体作为另外1个结构体或者对象的1个属性的时候,不能直接{}赋值.
CGRect rect;
rect.origin = (CGPoint){10,20};
rect.size = (CGSize){100,30};

2).也提供了函数来快速的创建GRect变量.
CGRect rect = CGRectMake(10, 20,100, 30);
CGRect rect1 =NSMakeRect (10,20,100,30);

NSNumber

是Foundation框架中定义好的1个类.这个类的对象的作用就是用来包装基本数据类型的。
将基本数据类型存储到NSArray数组中的步骤:1.先将基本数据类型包装到NSNumber对象中.
2.再将NSNumber对象存储到NSArray数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
NSNumber *number1 = [NSNumber numberWithFloat:10.1f];
NSNumber *number2 = [NSNumber numberwithFloat:10.2f];
NSNumber *number3出(NSNumber numberWithFloat:10.3f];
NSArray *arr = @[number1, number2, number3];
for (NSNumber *num in arr){
NSLog(@"%f" , num. floatValue);
}

创建NSNumber对象的简写方式:
@10;//代表是1个NSNumber对象,这个对象中包装的是整形的10 等于 [NSNumber numberWithInt:10];
包装注意:
如果后面的数据是1个变量那么这个变量就必须要使用小括弧括起来。
@(num):

NSValue

NSValue类的对象就是用来包装结构体变量【NSRange、CGPoint、CGSize、CGRect,该变量是无法存储到集合之中.】的。
解决方案:
先将这写结构体变量存储到0C对象中,再将0C对象存储到集合之中.

注意:NSValue存储不可变OC对象, 如果存入结构体,仅能只读;或者存入结构体的地址,利用二级指针来分配结构体内存并初始化【手动分配、释放内存】。

1
2
3
4
5
6
7
8
9
10
11
12
13
CGPoint p1 = CGPointMake(10, 20);
CGPoint p2 =CGPointMake(110, 120);
CGPoint p3 =CGPointMake(110, 320);
CGPoint p4 =CGPointMake(110, 240);
NSValue *v1 = [NSValue valueWithPoint:p1];
NSValue *v2 = [NSVa lue va lueWithPoint:p2] ;
NSValue *v3 = [NSValue valueWithPoint:p3];
NSValue *V4 = [NSValue va LueWithPoint:p4] ;
NSArray *arr = @[v1,v2,v3,v4];
for(NSValue *V in arr)
{
NSLog(@"%s@" ,NSStringF romPoint(v.pointValue));
}

CA【core animation】

CALayer

NSDate

NSDate取到的时间是当前系统的格林威治时间,0时区的时间.
NSDateFormatter转换成字符串以后会转换为当前系统的时区的时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
NSDate *date = [NSDate date] ;
NSLog(@"*@" , date);
2).格式化输出日期。指定日期输出的格式
默认的格式年-月-日时:分:秒+时区.
NSDate *date = NSDate date];
NSLog(@"%@" , date) ;
//1.先要创建1个NSDateFormatter对象,这个对象作用:将1个日期转换成1个指定的格式.
NSDateFormatter *formatter = [ [NSDateFormatter alloc] init];
//2.告诉这个日期格式化器对象要把日期转换个什么样子的.
yyyy:年份
MM:月份.
mm:分钟。
dd:天.
hh: 12小时.
HH: 24小时
ss:秒
formatter.dateFormat = *"yyy年MM月dd日 HH点mm分ss秒";
//3.使用日期格式化器将指定的日期转换指定格式的字符串.
NSString *str =[formatter str ingF romDate:date] ;
NSLog(@"str = %@" ,str);
-(NSString *)stringFromDate: (NSDate *)date; //将 日期类型换换为字符串
-(NSDate *)dateF romString: (NSString *)string;//将字符串转换为日期对象。

//计算时间
1.想得到明天此时此刻的时间。
当前时间的基础之,上+ 1天的时间。
在当前时间的基础之上,新增指定的时间.得到的1个新的时间。
+ (instancetype) dateWithTimeIntervalSinceNow: (NST imeInterval)secs ;
在当前的时间的基础之上,新增指定的秒。后的时间
得到东八区的时间:
NSDate *d1 = [NSDate dateWithTimeIntervalSinceNow: 8*60*60];
传入1个负数就是在当前时间的基础之,上减指定的秒数.
2.求两个时间之间的差。
可以实现的效果。 就是可以计算出执行代码所花费的时间。
- (NSTimeInterval)t imeIntervalSinceDate: (NSDate *) anotherDate;

//得到NSDate中的年 月日时分秒。
1.得到时间的各个部分。可以使用日期格式化器来得到.
NSDate *date = [NSDate date] ;
NSDateFormatter *formatter = [NSDateFormatter new] ;
formatter. dateFormat = @"HH" ;
NSString *str = [formatter stringF romDate:datel;
int year = str. intValue;
NSLog(@"year = %d" ,year);

2.日历对象。
NSDate *date = [NSDate date] ;
//1.创建1个日历对象。调用类方法currentCalendar得到1个日 历对象.
NSCalendar *calendar = [NSCaLendar currentCalendar] ;
//2.指定日历对象取到日期的对象的那些部分。是要取那1个时间对象的部分.
//返回1个日期组件对象.这个对象中就有指定日期的指定部分,
NSDateComponents *com = [caLendar components:NSCalendarUnitYear|NSCalendarUnitMonth |NSCalendarUnitDay fromDate: date];
NSLog(@"%ld-%ld- %1d",com.year,com.month,com.day,com.weekday); // weekday 周日为1,周六为7

【待补充】懒加载

swift懒加载(lazy load)VS OC懒加载

常用函数

随机数

1
arc4random()%x+y :意思是随机生成y到y+x之间的随机整数。不包括(y+x)

BUG

Type name does not allow storage class to be specified

在oc中,static不能修饰实例变量【即类属性/类方法】

只能这样用:

1,在.h中定义,在导入该.h的.m中使用

2, 在.m中定义使用,可以在方法里定义使用(使局部变量保留多次调用一个方法所得的值)

Unable to simultaneously satisfy constraints.

约束不适配,待处理【调试】

补充

属性命名的规范

声明属性时,命名前面下划线,而对应set/get方法名为set/get+去掉下划线的属性名首字母大写。

1
2
eg: NSString * _name; 
setName:NSString * name;

类方法的规范

提供与自定义构造方法相同功能的类方法【用来让外界调用类方法来快速的得到1个对象】,且由该类方法构建的对象在方法中就已经被autorelease【该对象就会被自动的加入到自动释放池】。

1
2
3
4
5
6
7
8
9
10
11
12
//对应实例方法 -(instancetype)init;
//autorelease 在ARC模式下不要写
+(instancetype)person
{
return [[[self alloc] init] autorelease];
}
//带参构造对象的类方法
//对应实例方法 -(instancetype)initWithName:(NSString *)name andAge:(int)age andWeight:(float)weight;
+(instancetype)personWithName:(NSString *)name andAge:(int)age andWeight:(float)weight
{
return [[[self alloc] initWithName:name andAge:age andWeight:weight] autorelease];
}

项目ARC模式与MRC模式兼容

ARC模式项目中部分文件使用MRC模式

TARGETS->Build Phases -> Compile Sources ->选择指定文件 补充命令 -fno-objc-arc

整个项目MRC模式转换为ARC模式(新手别碰)

Edit->Convert->To Objective-C ARC 【过程不可逆,需备份】

VMware16安装macOS12手把手教程

---------------- 本文结束 ----------------

本文标题:Objective-C语法点

文章作者:Pabebe

发布时间:2021年11月10日 - 17:10:18

最后更新:2022年05月07日 - 19:44:54

原始链接:https://pabebezz.github.io/article/d97699bf/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%