.

iOS开发几年了,你清楚OC中的这些东西

前言

几年前笔者是使用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{

}

...

end

21、但是在使用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,因为这只是写一点点语法的东西。









































北京哪里治白癜风好
北京白癜风最佳最好的治疗方法



转载请注明:http://www.linkchina365.com/jbzs/7051.html