接触iOS也有一阵子了,也参与到项目做了有些事情,主要是从改bug入手,边学边做,下面记录一下自己在学习中遇到一些点。
Category
Objective-C提供了一种与众不同的方式——Category,可以动态的为已经存在的类添加新的行为。这样可以保证类的原始设计规模较小,功能增加时再逐步扩展。使用Category对类进行扩展时,不需要访问其源代码,也不需要创建子类。Category使用简单的方式,实现了类的相关方法的模块化,把不同的类方法分配到不同的分类文件中。
1 | SomeClass.h |
这里还有一个约定成俗的习惯,将声明文件和实现文件名称统一采用“原类名+Category”的方式命名。
Protocol
Protocol,简单来说就是一系列不属于任何类的方法列表,其中声明的方法可以被任何类实现。这种模式一般称为代理(delegation)模式。你通过Protocol定义各种行为,在不同的场景采用不同的实现方式。在iOS和OS X开发中,Apple采用了大量的代理模式来实现MVC中View和Controller的解耦。
1 | @protocol ProcessDataDelegate <NSObject> |
@required和@optional,表示如果要实现这个协议,那么processSuccessful方法是必须要实现的,submitOrder则是可选的 如果不注明,那么方法默认是@required的,必须实现。
用尖括号(<…>)括起来的ProcessDataDelegate就是创建的Protocol。如果要采用多个Protocol,可以在尖括号内引入多个Protocol名称,并用逗号隔开即可。例如<ProcessDataDelegate,xxxDelegate>。
Protocol本身是可以继承的。
1 | @protocol A |
OC中没有方法的重载
OC中没有方法的重载!OC中没有方法的重载!OC中没有方法的重载!重要的事情说三遍~
遇到个问题一直不能理解,原来就是因为OC没有方法的重载!在Java中,可以根据参数的个数、类型来重载方法,在OC中没有这一说。
1 | -(return_type) instance_method1; // 无参数 |
看到2个参数的例子,在参数前面会有andPar
,这个结合instance_method3
才构成一个方法。调用的时候:
1 | [obj instance_method1]; |
数组
- NSArray:不可变数组,NSArray保存的对象可以是不同的对象。但只能保存对象,int ,char,double等基本数据类型不能直接保存,需要通过转换成对象才能加入数组。 [array count] : 数组的长度。 [array objectAtIndex 0]: 传入数组脚标的id 得到数据对象。 [arrayWithObjects; …] :向数组对象初始化赋值。这里可以写任意对象的指针,结尾必须使用nil。
- NSMutableArray:可变数组,[NSMutableArray arrayWithCapacity:6] :初始化可变数组对象的长度,如果后面代码继续添加数组超过长度6以后NSMutableArray的长度会自动扩充,6是自己可以设置的颗粒度。 [array addObject:…] : 向可变数组尾部添加数据对象。 [array addObjectsFromArray:..] :向可变数组尾部添加一个数组对象。
block
block,带有自动变量的匿名函数,回调,用^
表示。
1 | int b = 0; |
虽然在调用blo之前改变了b的值,但是输出的还是Block编译时候b的值,所以截获瞬间自动变量就是:在Block中会保存变量的值,而不会随变量的值的改变而改变。
1 | int b = 0; |
这段代码编译出错,编译器提示的大概就是不能在Block中改变变量的值。因为在Block中截获了变量的瞬间值以后就不能再改变变量的值,如果想要在Block中改变变量的值,那么只需要在变量声明的时候加上__Block修饰符,像这样:
1 | __block int b = 0; |
然而这样的情况又是允许的:
1 | NSMutableArray *array = [[NSMutableArray alloc]init]; |
为什么呢,因为只是对截获的变量进行了操作,而没有进行赋值,所以对于截获变量,可以进行操作而不可以进行赋值。
self 与 _
定义个属性a
:
1 | @interface Test:NSObject |
直接用属性名访问:
1 | -(void) changeAValue:(int) newValue { |
通过self.a的形式访问:
1 | -(void) changeAValue:(int) newValue { |
这两种访问方式有区别吗?答案是肯定的。
通过第一种方式访问,其实是类似于C++的访问方式,是直接访问的实例变量并赋值。而第二种方式,并不像其表面那么直观,它其实是通过调用编译器自动生成的对于a变量的赋值函数来实现的。即:
1 | -(void) changeAValue:(int) newValue { |
类的属性仅在本类中可以访问,子类无法通过_a的形式访问。但是可以通过继承父类的存取方法访问。
resignFirstResponder
这个方法是取消第一响应者状态的。如果对textfield使用的话,那么调用这个方法,textfield的第一响应者状态就会取消,然后键盘就消失了。
delegate模式
类似于Android中的listener。系统提供各种interface,然后调用interface中的方法,至于具体实现,可以由我们自己定义。
Android:
1 | editText.addTextChangedListener(new TextWatcher() { |
iOS:
1 | YourClass ()<UITextFieldDelegate> |
UITextFieldDelegate就类比于TextWatcher,它内容如下:
1 | @protocol UITextFieldDelegate <NSObject> |
通过_mobileTextField.delegate = self就类似于添加了listener,然后实现相应的方法,就可以做自己的事情了。
权限
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data.
意思就是没有配置权限,需要在 info.plist 文件添加一个 NSPhotoLibraryUsageDescription的 key,然后添加一个描述。
1 | <key>NSPhotoLibraryUsageDescription</key> |
dispatch_async
线程相关的语法:
1 | dispatch_async(queue, ^{ |
实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型:
1 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ |
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。
dispatch_get_main_queue(); //获得主线程的dispatch队列。
UITextFiled
类似Android中的EditText。可以使用leftView、rightView来实现padding效果,以及编辑状态下添加删除按钮。
1 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { |
也可以直接使用layer来实现shader:
1 | _titleTextField = [[UITextField alloc]initWithFrame:CGRectMake((self.width - 170) / 2, 0, 170, self.height)]; |
拼接字符串
1 | NSDate * senddate=[NSDate date]; |
也可以使用NSMutableString
。