OC特点
OC没有命名空间,对象的成员变量是默认受保护的。
弱类型语言,单继承
init系统自带的初始化方法,默认是给基本数据类型赋值0,0.0,指针赋值NULL,对象指针类型赋值nil 本质上都是(void *)0
[CLASS new]
[[CLASS alloc]]init]
[类型/对象 方法]
1 | 重写init |
【待补充】实例对象、类对象(class object)与元类对象(metaclass object)
实例对象:当我们在代码中new一个实例对象时,拷贝了实例所属的类的成员变量,但不拷贝类定义的方法。调用实例方法时,根据实例的isa指针去寻找方法对应的函数指针。
类对象:是一个功能完整的对象。特殊之处在于它们是由程序员定义而在运行时由编译器创建的,它没有自己的实例变量(这里区别于类的成员变量,他们是属于实例对象的,而不是属于类对象的,类方法是属于类对象自己的),但类对象中存着成员变量与实例方法列表。
元类对象:OC 的类方法是使用元类的根本原因,因为其中存储着对应的类对象调用的方法即类方法。其他时候都倾向于隐藏元类,因此真实世界没有人发送消息给元类对象。元类的定义和创建看起来都是编译器自动完成的,无需人为干涉。要获取一个类的元类,可使用如下定义的函数:
1 | Class objc_getMetaClass(const char* name); //name为类的名字 |
此外还有一个获取对象所属的类的函数:
1 | Class object_getClass(id obj) ; |
由于类对象是元类的实例,所以当传入的参数为类名时,返回的就是指向该类所属的元类的指针。
参考文章:
【待补充】消息机制
总结取到类在代码段中的地址的方式.
1.调试查看对象的isa指针的值.
2.在类方法中查看self的值.(相当于C++的this, 但在对象方法【动态分配内存,调用完成后会释放内存】中,self不可以以调用类方法,因为此时self代表对象;在类方法【在应用程序开始运行时一直驻于内存】中,self代表类,不可以调用对象方法与属性,只能通过实例化一个对象来调用对象方法与属性。可以总结:在实例方法中self是object,而在类方法中self是class,所以在两种方法内部无法通过self来相互调用,self只能调用相同类型的方法。)
3.调用对象的对象方法class 就会返回这个对象所属的类在代码段中的地址.
4.调用类的类方法class 就会返回这个类在代码段中的地址.
1 | Class c1 = [类名 class]; //此时 c1 等价于 该类,只能调用该类的类方法 |
私有属性:在implementation{}中声明【这样Xcode不会提示】
私有方法:在类中只实现,不声明
类与方法在代码段中的存储方式与调用机制
Class对象与SEL对象定义过程中便已经是指针了。
(1)类以 Class对象的形式存储在代码段中,包括类名、方法、属性、isa指针(继承于NSObject【包含了NS类中所有的基本属性和方法】)。
当对象的实例方法被调用,是通过isa来找到对应的类,然后在该类的class_data_bits_t中去查找方法。class_data_bits_t是指向了类对象的数据区域。在该数据区域内查找相应方法的对应实现。
(2)方法存储于SEL对象【selector 选择器】中,再将该SEL对象作为类对象的属性
1 | SEL s1 = @selector(方法名); |
调用的本质是利用OC的消息机制,即给类对象发送SEL消息。
点语法
对象.去掉下划线的属性 本质是转换为 调用对应属性的getter/setter方法
即[对象 set/get去掉下划线的属性名首字母大写(:数据)]
这就是命名规范的重要性【属性前面下划线,set/get去掉下划线的属性名首字母大写】。
当不存在set/get函数或set/get函数命名有误,则不能使用点语法
NSLog与printf的区别
printf(暴力调试)
NSlog(检测当前程序,添加了时间戳,工程名字,进程号,线程号,自动换行)
1 | NSLog(@"%@",CLASSNAME.obj) |
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 | #import <Foundation/Foundation.h> |
属性
合成存取器【属性是为成员变量服务的】
1 | @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 | @property(nonatomic,assign,getter=isMan,setter=XYZ:)BOOL sex; |
类的扩展
分类
将一个类分为多个模块,或者为已经存在的类添加方法
1 | @interface 类名(分类名) |
注意:
1.分类只能增加方法,不能增加属性
2.在分类之中可以写@property, 但是不会自动生成私有属性,也不会自动生成getter setter的实现。
只会生成getter setter的声明。 所以,需要在本类中定义属性且 分类中完成getter 和setter的声明。
3.在分类的方法实现中不可以直接访问本类的真私有属性(定义在本类的@implementation之中)但是可以调用本类的getter setter来访问属性。
即本类的@property生成的私有属性,只可以在本类的实现中访问。分类中不能直接访问真私有属性,但可以使用 getter setter 来访问。
4.当分类中有和本类中同名的方法的时候,优先调用分类的方法,哪怕没有引入分类的头文件。如果多个分类中有相同的方法,优先调用最后编译的分类。【TARGETS->Build Phases -> Compile Sources 查看文件编译顺序】
延展
是一个特殊的分类,没有名字,没有实现。
只有声明,和本类共享一个实现。
1 | @interface 类名() |
主要用途:(真)私有化类的成员
eg:要为类写1个私有的@property.且生成getter、setter方法只能在类的内部访问 不能在外部访问。
解决方案:延展100%的情况下不会独占1个文件,都是将延展直接写在本类的实现文件(常见于ios UI 私有控件)中。这个时候,写在延展中的成员,就相当于是这个类的私有成员。只能在本类的实现中访问。外部不能访问。
真私有属性
如果想要为类写1个真私有属性,虽然我们可以定义在@implementation之中,但是不要这么写 这样很不规范。写1个延展。将这个私有属性声明在延展中.
如果要为类写1个私有方法,建议将声明写在延展中,实现写在本类的实现中,提供代码的阅读性如果想要为类写1个私有的@property 就直接写在延展就可以了,
1 | 实现方式一:【不推荐】 |
延展和分类的区别
1.分类有名字,延展没有名字 是1个匿名的分类。
2.每1个分类都有单独的声明和实现,而延展只有声明 没有单独的实现 和本类共享1个实现,
3.分类中只能新增方法,而延展中任意的成员(属性、方法)都可以写.
4.分类中可以写@property 但是只会生成getter setter的声明。
延展中写@property 会自动生成私有属性也会生成getter setter的声明和实现。
block
block 是一种存储一段代码的数据类型,类似于C++函数指针。
1 | 返回值类型 (^block变量名)(参数列表) = ^返回值类型(参数列表){ |
block简写规则
代码段简写
1.如果代码段没有返回值。那么代码段的void可以省略,
2.如果我们写的代码段没有参数,那么代码段的小括弧写可以省路。
1 | //综上两条 |
3.无论代码段是否有返回值,在写代码的时候省略返回值类型。
这个时候系统会自动的确定返回值的类型【代码段中没有返回任何数据为void,有返回数据则为对应类型】。
声明block变量简写
4.声明block变量的时候,如果有指定参数,可以只写参数的类型而不写参数的名称;
1 | //综上两条 |
block访问外部变量
1.在block代码块的内部可以读取定义在外部的变量的值【包括全局变量和在外部的局部变量】。
2.在block代码块的内部可以修改全局变量的值,但是不能修改在外部的局部变量的值【若要修改,该变量需添加_ _block
的修饰符】。
block与函数的区别
相网点:都是封装1段代码,
不同点:
- block是1个数据类型。 函数是1个函数。
- 我们可以声明black类型的安量函数就只是函数。
- block可以作为函数的参数。而函数不能直接作为函数的参数.
协议protocol
专门用来声明一大堆方法【.h并且只有1个.h文件.】。(不能声明属性,也不能实现方法,只能用来写方法的声明).只要某个类遵守了这个协议.就相当于拥有这个协议中的所有的方法声明。相当于C++中的纯虚函数
类是单继承,可以多协议
1 | @protocol协议名称<NS0bject> |
协议与协议之间可以相互继承
NSObject协议被NS0bject类遵守(类的名称可以和协议的名称一致)
要求所有的协议都必须直接的或者间接的从NS0bject基协议继承。
1 | //子协议中不仅有自己的方法的声明,还有父协议中的所有的方法的声明.如果1个类遵守了某份协议,那么这个类就拥有这个协议和这个协议的父协议中的所有的方法声明。 |
协议类型限制指针
声明1个指针变量,要求这个指针变量指向的对象必须遵守多个协议。
1 | // obj1是遵守StudyProtocol, SBProtocol协议的指针,其指向的对象也必须遵守StudyProtocol, SBProtocol协议。 |
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 | //声明协议 |
检查
编译检查:在编译时,能不能调用对象的方法主要看指针所属类型。(看左边)
运行检查:在运行时,能不能调用对象的方法主要看指针所指向对象类型。(看右边)
C++的多态性内部实现—迟绑定
1 | 1).判断对象中是否有这个方法可以执行.【实例方法】 |
内存管理
只需要管理存储在堆上的OC对象的生成与释放(本质是所占用内存的权限变更)
引用计数
1 | 1).为对象发送1条retain消息。对象的引用计数器就会加1.当多1个人使用对象的时候才发。 |
僵尸对象
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 | @autoreleasepool { |
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 | typedef enum { |
NS_ENUM是一个OC中的宏,可以判断编译器能否采用新式枚举:如果不能,那么效果等同于仅仅使用typedef的enum;如果能够采用新式枚举,那么NS_ENUM所定义的枚举类型,就是处理后的enum类型,可以在使用typedef的同时,指定底层数据类型。
1 | //类型【只能是基础类型】,枚举变量名 |
按位或 枚举的作用
NS_OPTIONS也是OC中的一个宏,在非C++ 编译模式下,NS_OPTIONS和NS_ENUM是一样的。对于C++模式编译,NS_OPTIONS会做一些特殊处理,保证枚举结果的类型正确。
在用或运算处理两个枚举类型时,C++ 认为枚举结果的数据类型应该是枚举的底层数据类型(即NSUInteger),而且C++不允许将这个底层类型隐式转换成枚举类型本身。
1 | typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) { |
NSString
NSString 不可变字符串,即当在内存中创建1个字符串对象以后.这个字符串对象的内容就无法更改【无论在常量区还是堆区】。
1 | NSString *str = @"jack"; //0C字符串常量存储在常量区(数据段) |
注意点
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 | 1.使用拼接的方式创建1个NSString对象. |
NSMutableString是NSString的子类.
NSMutableString从NSString继承.存储在NSSMutableString对象中的字符串数据可以更改【不会新创建对象】.具备可变性,
NSMutbaleStrint: 做大批量的字符串拼接【10次以上】的时候才使用。
1 | NSMutableString *str = (NSMutableString string]; |
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 | NSArray *arr = [NSArray arrayWithobjects:@"jack" ,nil,@"rose" ,@"lili",nil]; |
c.每1个元素都是紧密相连的。每1个元素仍然有自己的下标。
d.元素的类型是id类型的。
常用方法
1 | //取值 |
NSMutableArray是NSArray的子类.
- NSMubleArray仍然是1个数组.具备NSArray数组的特点。
只能存储OC对象.每1个元素的紧密相连的。 - NSMutableArray相对于父类做的扩 展:NSMutableArray数组的元素可以动态的新增和删掉。
其他的用法均与NSArray-一致。
1 | NSMutableArray *arr1 = [NSMutableArray new]; |
NSMutableArray的删除注意事项
来源:iOS小知识(2016.07)—NSMutableArray中删除对象知多少
removeObjectAtIndex:删除指定NSMutableArray中指定index的对象,注意index不能越界。【最多只能删除一个对象】
removeObject:删除NSMutableArray中所有isEqual:待删对象的对象可以删除多个对象(只要符合isEqual:的都删除掉)
不要在for in 循环中删除数组内部对象,“易引发崩溃” 大致于C++迭代器失效类似原因。
解决方案:
(1)倒序删除
1 | - (void)removeObjectsReversed |
(2)先通过indexesOfObjectsPassingTest:把待删除对象的index找出来,再调用removeObjectsAtIndexes:进行一次性删除。
1 | - (void)removeObjectsUseEnumration |
NSDictionary
1).这个数组是以键值对的形式存储数据的每往这个数组中存储1个数据,就要求为存储的数据取1个别名.
2).别名必须是遵守了NSCopying协议的对象。NSString就是遵守 了这个协议的。存储的数据只能是任意的0C对象。
3).长度固定.NSDictionary数组一旦创建 ,其长度就固定,键值对无法新增和删除。
1 | //NSDictionary数组的创建 . |
常用方法
1 | //取值【往字典数组中存储键值对的时候键不允许重复.】 |
【待补充】引出nonnull / nullable、_Nonnull / _Nullable、nonnull / nullable区别 &&nil、Nil、NULL、NSNull区别 - 简书 (jianshu.com)
block遍历中出现_Nonnull
存储原理
并不是按照顺序挨个挨个的存储的。存储键值对的时候,会根据键和数组的长度做1个哈希算法.算出1个下标。将这个键值对存储在该下标处.【NSArray存储效率更高】
取值的时候:也是根据键做1个哈希算法.就可以算出这个键值对存储的下标然后直接找到这个下标的数据取出.
NSMutableDictionary是NSDictionary的子类 .
所以NSMutableDictionary也是1个字典数组,也是以键值对的形式存储数据的.存储在其中的元素可以动态的新增和删除。
1 | 1.创建可变字典数组。 |
NSFileManger
NSFileManger用来操作磁盘上的文件文件央对他们进行创建、 删除、复制拷贝移动…..
这个类的对象是以单例模式创建的.
1 | //得到NSFileManager的1个单例对象。 |
单例模式
实现单例模式【1个类的对象,无论在何时创建也无论在什么地方创建也无论创建多少次.创建出来的都是同1个对象.
】
重写+ allocWithZone:
无论如何创建对象,最终都会调用alloc方法来创建对象。
1),alloc方法的内部。 其实什么都没有做,只是调用了allocWithZone:方法。
2),实际上真正申请空间创建对象的事情是allocWithZone:方法在做。
1 | // 懒汉模式 |
单例模式的规范:如果类是1个单例模式.要求为类提供1个类方法.来返回这个单例对象。
类方法的名称必须以shared类名; default类名;
常用方法
1 | //判断. |
CG【core graphic】
CGPoint
这个结构体一般情况下是用来表示坐标的,用来表示控件在界面上得位置.
CGPoint与NSPoint都是同1个结构体,只不过定义两个名字.
1 | //定义 |
CGSize
声明1个变量来保存某个控件的大小。
1 | //定义 |
CGRect
这个结构体变量一般情况下存储1个控件的位置和大小。
1 | //定义 |
NSNumber
是Foundation框架中定义好的1个类.这个类的对象的作用就是用来包装基本数据类型的。
将基本数据类型存储到NSArray数组中的步骤:1.先将基本数据类型包装到NSNumber对象中.
2.再将NSNumber对象存储到NSArray数组中。
1 | NSNumber *number1 = [NSNumber numberWithFloat:10.1f]; |
NSValue
NSValue类的对象就是用来包装结构体变量【NSRange、CGPoint、CGSize、CGRect,该变量是无法存储到集合之中.】的。
解决方案:
先将这写结构体变量存储到0C对象中,再将0C对象存储到集合之中.
注意:NSValue存储不可变OC对象, 如果存入结构体,仅能只读;或者存入结构体的地址,利用二级指针来分配结构体内存并初始化【手动分配、释放内存】。
1 | CGPoint p1 = CGPointMake(10, 20); |
CA【core animation】
CALayer
NSDate
NSDate取到的时间是当前系统的格林威治时间,0时区的时间.
NSDateFormatter转换成字符串以后会转换为当前系统的时区的时间。
1 | NSDate *date = [NSDate date] ; |
【待补充】懒加载
常用函数
随机数
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 | eg: NSString * _name; |
类方法的规范
提供与自定义构造方法相同功能的类方法【用来让外界调用类方法来快速的得到1个对象】,且由该类方法构建的对象在方法中就已经被autorelease【该对象就会被自动的加入到自动释放池】。
1 | //对应实例方法 -(instancetype)init; |
项目ARC模式与MRC模式兼容
ARC模式项目中部分文件使用MRC模式
TARGETS->Build Phases -> Compile Sources ->选择指定文件 补充命令 -fno-objc-arc
整个项目MRC模式转换为ARC模式(新手别碰)
Edit->Convert->To Objective-C ARC 【过程不可逆,需备份】