前言
几年前笔者是使用Objective-C进行iOS开发,不过在两年前Apple发布swift的时候,就开始了swift的学习,在swift1.2发布后就正式并且一直都使用了swift进行iOS的开发了,之后就是对swift持续不断的学习,近来swift3.0的发布,更多的人会选择swift来进行iOS的开发看上去更是成为了一种趋势,不过一个合格的iOS开发者对oc以及c语言的掌握是必不可少的技能,本篇中主要是写一些大家平时都可能用到但是不一定知道的oc的东西
1、oc中的对象的创建:首先会通过+(id)alloc动态的分配所有的变量以及父类定义的变量所需要的足够内存,同时会清除所有的分配的内存空间,全部置为0
2、同时接着需要调用class的-(id)init方法,这个方法给每个变量设置初始值
3、返回的类型为id,id是一个可以指向任意类型的指针(不用*号),这个在一定程度上可以完成多态的效果
4、对oc中的class文件的理解:class,extension,category
ZJPerson.h文件
Snip_4.png
ZJPerson.m文件
Snip_5.png
ZJPerson.m文件
m
5、[[XXObjectalloc]init]初始化方法不需要参数的时候,和[XXObjectnew]方法相同
6、通过字面量来初始化对象,例如
NSString*string=
"string";==[[NSStringalloc]initWithString:"string"];等初始化方法NSNumber*myBOOL=
YES;==[[NSNumberalloc]initWithBool:YES];NSNumber*myFloat=
3.14f;==NSNumber*myInt=
42;==NSNumber*myLong=
42L;==...7、oc(c)中多行宏的定义(这个在swift…中更方便直接一个全局的函数就搞定了):在除了最后一行的每一行结尾加一条反斜杠\
定义.png
使用.png
8、比较是否相同:使用if(a==b){},如果a,b是对象类型,那么比较的是指针是否相同,而不是比较值是否相同,如果a,b是基本类型(int,double…),那么比较的是值是否相同;使用if([aisEqual:b]){},则比较的是a,b的值是否相同
9、初始化基本类型的时候尽量设置初始值,因为编译器分配的初始值并不确定,但是对象类型会默认初始化为nil
10.条件判断:当对象不为nil(有内存地址)的时候,或者基本类型非0,或者bool类型为true,这个时候条件都为真,其他情况条件为假
11.oc中属性的getter和setter
property(nonatomic)NSString*name;例如当有这样一个name属性的时候,默认是readWrite的,编译器会自动生成一个set(setName:)和get(-(NSString*)name)方法,这个时候可以通过set或者get方法访问到name,如果申明为(readonly),那么将只会生成get方法
[selfsetName:
"setname"];NSString*getName=[selfname];
也可以通过点语法访问(实际上是会自动调用set和get方法)
self.name=
"setname";NSString*dotName=self.name;
同时你可以重写name的get(懒加载…)和setter(拦截set方法)…对应name属性,编译器会生成(synthesize)一个_name允许我们直接通过指针访问变量,而不会调用get方法,所以通过_xx访问的变量不会调用懒加载(get方法),所以在写懒加载方法的时候,不能使用self.xx(造成死循环),而要使用_xx
-(NSString*)name{
//这里面不能使用self.name,因为点语法会调用这个get方法,造成死循环
if(_name==nil){
_name=
"name";}
return_name;
}
同时这个synthesize的名字我们是可以自己修改的,使用如下的语法
synthesizename=customName;那么这个时候就不能通过__name访问到name了,因为我们已经指定了通过customName才能访问到了NSString*getName=customName;
当然如果,你是这样写的
synthesizename;,并没有指定名字,这个时候访问的时候就直接使用变量名而不需要加下划线(_)了name="setname";??这个时候就比较爽了,和swift,java这些一样,不需要self,this了;12、oc的属性默认是atomic(原子的),也就是说是线程安全的,这个时候是不允许重写set和get方法的,因为内部的setter和getter会做出处理,保证线程安全,但是我们经常使用的是noatomic,因为访问的速度比较快,并且可以自己重写getter和setter
13、oc中的对象是动态管理(内存)的,是分配在heap(堆)上所以需要一个指针来指向它(才能访问),所以对象类型需要用星号NSString*str;
14、oc中的对象管理在ARC下是用引用计数来管理的,当有一个强引用对象A指向这个对象B的时候,B引用计数加一,当这个对象A销毁的时候,B的引用计数减一,直到B的引用计数为0的时候就被自动销毁,当然这个时候如果A强引用B,B同时强引用A就造成了循环引用,两者都不会被销毁,就造成了内存泄漏,解决方法是将一方标记为weak或者unsafe_unretained(垂悬指针,和swift中的[unownedself]类似,所以运用不当会造成野指针的问题)
15、oc中的属性默认是strong的,所以需要显示的指定为其他的(weak,unsafe_unretained…)
16、NSObject*__weaksomeObject=[[NSObjectalloc]init];,这个someObject没有对象强引用他,所以这行代码之后会立马被置为nil,NSObject*__weaksomeObject=self.someObject,这个someObject在这行代码之后不会立刻被置为nil,而是会在所在的代码块结束后被置为nil
17、对于属性的赋值(深浅拷贝)
property(nonatomic)NSString*name;NSMutableString*str=[NSMutableStringstringWithString:
"初始"];ViewController*ob=[ViewControllernew];
ob.name=str;----浅拷贝
NSLog(
"%",ob.name);---初始[strappendString:
"+1"];NSLog(
"%",ob.name);---初始+1这里出现ob.name改变的原因就是:属性name是strong(默认)类型的,ob.name=str;这行代码赋值后,实际上只是name强指向了str,所以当str的内容改变的时候,ob.name也改变了
NSLog(
"%",ob.name);---初始str=[NSMutableStringstringWithString:
"改变"];NSLog(
"%",ob.name);---初始但是这样的赋值,直接改变str之后并不会影响原来的str的指针指向的内容,所以ob.name仍然指向原来的str,因此内容并未改变
如果将上面的ob.name=str;改为ob.name=[strmutableCopy];那么将上面的两种操作都不会影响ob.name----深拷贝
如果name被修饰为copy
property(nonatomic,copy)NSString*name;那么上面的操作都不会改变ob.name的内容----深拷贝
18、分类(category)定义的函数和属性在运行时中和原生的class中定义的东西并没有区别Atruntime,there’snodifferencebetweenamethodaddedbyacategoryandonethatisimplementedbytheoriginalclass
19、不过分类中定义的属性,编译器并不会自动生成getter和setter,以及_XX变量来访问,需要自己提供getter和setter,并且需要使用运行时才能绑定这个属性到这个类中,实现原生类中定义的属性的效果
///例如可能是这样的使用
staticconstvoid*propertyKey=propertyKey;
///将value通过运行时绑定到self
objc_setAssociatedObject(self,propertyKey,value,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
///将value在运行时中通过propertyKey取出绑定的值
idvalue=objc_getAssociatedObject(self,propertyKey);
20、同时分类也可以用来将一个复杂的类中的代码分块(swift的extension可以有相似的作用),使得代码组织更好,例如可以将tableView的delegate,和Datasource在分类中实现,
implementationViewController(tableview)-(void)tableView:(UITableView*)tableViewdidSelectRowAtIndexPath:(NSIndexPath*)indexPath{
}
...
end21、但是在使用category来扩展Cocoa的原生类的时候,要注意函数的命名如果是和原生已有的函数名相同,那么将会发生不可预料的结果(不能确定哪一个方法在运行时会被调用),因此建议在自己的函数名前面加上前缀,就像重写+load()来实现各种黑魔法的时候也是可能会发生不可预料的结果,因为同一个项目中可能有多个地方重写了这个类的+load方法
22、初始化NSArray的时候,如果通过NSArray*arr1=
[object1,object2];,不需要以nil结尾,如果通过构造方法初始化,则需要传入nil结尾,同时,如果中间的对象有nil,那么将在中间nil就结束了,NSArray*arr2=[NSArrayarrayWithObjects:object1,object2,object3,nil,object4,object5,nil]这个arr2只可能会存储第一个nil前的对象23、如果在数组中一定要存储nil,那么只能用NSNull来代替
24、如果NSArray中存储的是NSArray,NSDictionary,NSString,NSData,NSDate,NSNumber这些类型的对象,那就可以直接写入disk并且读取disk的数据做持久化数据操作[arraywriteToURL:fileURLatomically:YES],但是如果是有其他的类型,就需要使用归档来实现了
25、在for-in快速枚举中,不能够修改(增删)被枚举的对象(数组,字典,集合)
26、在写代码的时候,进行条件判断的时候,经常会出现这样的代码if(a=1){...},这样写编译器是会报错的,需要写成if(a==1){...},当然你非要使用一个等号也是可以的,需要额外加一个括号,if((a=1)){...}
27、实际上绝大多数情况下都是我们写条件判断的时候都是使用==,而非=,也就只有当我们写构造方法的时候才可能会写到=,像这样if(self=[superinit]){...},其实这并不是使用=来判断条件相等是正确的,只是在这里,通过[superinit]方法会返回一个id对象,通过,self=[superinit],把这个对象赋值给self,这个时候的if就是用来判断,被赋值后的self是否为nil,而不是self是否等于[superinit]返回的对象.
28、在oc中block是object类型的,所以是可以存储在NSArray…中,同时在调用block的时候,如果block为nil(未赋值),那么程序将crash.
29、oc中block可以捕获变量,什么意思呢—就是block会默认捕获到变量的值,在之后不受到原来变量的改变的影响,例如
intanInteger=42;
void(^testBlock)(void)=^{
NSLog(
"Integeris:%i",anInteger);};
anInteger=84;
testBlock();----输出的值仍然为42
30、第二种block捕获变量的方式,是捕获变量的指针,被捕获的变量值改变,则block中的变量值也改变了,不过需要对变量进行__block标记,例如上面的代码,只改变一点,结果就变了
__blockintanInteger=42;
void(^testBlock)(void)=^{
NSLog(
"Integeris:%i",anInteger);};
anInteger=84;
testBlock();---输出值这时是84
31、伴随着block能够捕获变量的能力的一个问题就是,循环引用,在ARC中,只要不是用到纯C语言的库,管理内存的工作都不需要我们完成,但是循环引用却是我们需要解决的,最常见的就是当block捕获的变量是一个对象的属性(方法)的时候,也就是会捕获到self,那么这个时候就可能会造成循环引用(block属性应该被标记为copy),解决方法也很简单,使用一个对self弱引用的指针即可,这个写法就很多了,笔者习惯的写法是:__weaktypeof(self)weakSelf=self;,那么在block中使用weakSelf替代self调用相关的属性或者方法,就不会造成循环引用
32、使用weakSelf能够解决block捕获self造成的循环引用的内存泄漏问题,但是带来的另一个问题就是,特别是在多线程中,可能在block中代码正在执行的时候,self被销毁了,因为使用weakSelf捕获到的是self的弱引用,那么后续的代码就不能够继续执行了,这个时候为了保证在这个block中self即使被销毁block里面的代码也能正常执行,我们需要的另一个操作就是,将weakSelf强引用一次,让他的引用计数加1,就能处理这个问题,就是Apple在wwdc中提到的weak-strong-dance,笔者习惯的书写方式是:__strongtypeof(self)strongSelf=weakSelf;,,,当然这个必须要明白的是,这个block里面的strongSelf能够保证里面代码执行完毕的前提是程序能够执行到block,如果在执行block之前self已经被销毁了,那么这个block肯定是不会被调用的(block的引用计数已经为0).
按照惯例,我的文章里面都会有demo的,但是这篇文章没有demo,因为这只是写一点点语法的东西。
北京哪里治白癜风好北京白癜风最佳最好的治疗方法