c++开发找实习,结果找到了WXG-iOS客户端开发,记录一下学习经历。
iOS开发基本还是使用Objective-C语言,该语言被认为是C语言的超集,赋予了C语言面向对象的能力,完全支持C/C++语言,但和C++语言的面向对象有很大不同,它是以消息机制为底的,比如可以面向向不包含该方法的对象发送该消息,可以通过编译,直到运行的时候才会报错。
1.OC对象的本质
OC中万物来源于NSObject,很多类都是继承自它。它的本质在源码中可以看到
struct NSObject_IMPL {
Class isa;
};
// 查看Class本质
typedef struct objc_class *Class;
我们发现Class其实就是一个指针,对象底层实现其实就是这个样子。
OC的类信息存放在哪里 OC对象主要可以分为三种
- instance对象(实例对象)
- class对象(类对象)
- meta-class对象(元类对象)
instance对象
就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
instance对象在内存中存储的信息包括
- isa指针
- 其他成员变量
class对象
我们通过class方法或runtime方法得到一个class对象。class对象也就是类对象。
成员变量的值时存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值。但是成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中。
每一个类在内存中有且只有一个class对象。可以通过打印内存地址证明
class对象在内存中存储的信息主要包括
- isa指针
- superclass指针
- 类的属性信息(@property),类的成员变量信息(ivar)
- 类的对象方法信息(instance method),类的协议信息(protocol)
元类对象 meta-class
每个类在内存中有且只有一个meta-class对象。
meta-class对象和class对象的内存结构是一样的,所以meta-class中也有类的属性信息,类的对象方法信息等成员变量,但是其中的值可能是空的。
在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的类方法的信息(class method)
关于isa、superclass
- instance的isa指向class
当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用。 - class的isa指向meta-class
当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用 - meta-class的isa指向基类的meta-class,基类的isa指向自己
class的superclass指向父类的class,如果没有父类,superclass指针为nil - meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class
- instance调用对象方法的轨迹,isa找到class,方法不存在,就通过superclass找父类
- class调用类方法的轨迹,isa找meta-class,方法不存在,就通过superclass找父类
2.委托(delegate)
这是一种设计模式,就是某个对象指定另一个对象处理某些特定任务的设计模式。通俗来说,就是“某个对象”把要做的事情委托给“另一个对象”去做。iOS客户端开发中感觉这是一个特色,就像QT开发中的信号槽机制,好像也类似QT的信号槽机制,用于传值。
- 比如:两个类之间的传值,类A调用类B的方法,类B在执行过程中遇到问题通知类A,这时候我们需要用到代理(Delegate)。
- 又比如:控制器(Controller)与控制器(Controller)之间的传值,从C1跳转到C2,再从C2返回到C1时需要通知C1更新UI或者是做其它的事情,这时候我们就用到了代理(Delegate)传值。
委托与代理
托方通过某种方式把任务分派出去给代理方处理,而两者之间的联系便是协议。
比如view视图一般作为委托方,controller作为代理方,controller实现view中的具体流程,举个例子在登录流程中,view提供用户名和密码传递给controller,controller类里面具体实现和后台交互等的验证流程等。
在程序中:一般情况下
1.委托需要做的工作有:
- 1.1定义协议与方法
- 1.2声明委托变量
- 1.3设置代理
- 1.4通过委托变量调用委托方法
2.代理需要做的工作有:
- 2.1遵循协议
- 2.2实现委托方法
3. block
概述
闭包 = 一个函数「或指向函数的指针」+ 该函数执行的外部的上下文变量「也就是自由变量」;Block 是 Objective-C 对于闭包的实现。
其中,Block:
- 可以嵌套定义,定义 Block 方法和定义函数方法相似
- Block 可以定义在方法内部或外部
- 只有调用 Block 时候,才会执行其{}体内的代码
- 本质是对象,使代码高聚合
使用 clang 将 OC 代码转换为 C++ 文件查看 block 的方法:
- 在命令行输入代码 clang -rewrite-objc 需要编译的OC文件.m
- 这时查看当前的文件夹里 多了一个相同的名称的 .cpp 文件,在命令行输入 open main.cpp 查看文件
Block的定义与使用
1、无参数无返回值
//1,无参数,无返回值,声明和定义
void(^MyBlockOne)(void) = ^(void){
NSLog(@"无参数,无返回值");
};
MyBlockOne();//block的调用
2、有参数无返回值
//2,有参数,无返回值,声明和定义
void(^MyblockTwo)(int a) = ^(int a){
NSLog(@"@ = %d我就是block,有参数,无返回值",a);
};
MyblockTwo(100);
3、有参数有返回值
//3,有参数,有返回值
int(^MyBlockThree)(int,int) = ^(int a,int b){
NSLog(@"%d我就是block,有参数,有返回值",a + b);
returna + b;
};
MyBlockThree(12,56);
4、无参数有返回值(很少用到)
//4,无参数,有返回值
int(^MyblockFour)(void) = ^{NSLog(@"无参数,有返回值");
return45;
};
MyblockFour();
5、实际开发中常用typedef 定义Block
例如,用typedef定义一个block:
typedef int (^MyBlock)(int , int);
这时,MyBlock就成为了一种Block类型
在定义类的属性时可以这样:
@property (nonatomic,copy) MyBlock myBlockOne;
使用时:
self.myBlockOne = ^int (int ,int){
//TODO
}
Block与外界变量
1、截获自动变量(局部变量)值
(1)默认情况
对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的。也就是说block的自动变量截获只针对block内部使用的自动变量, 不使用则不截获, 因为截获的自动变量会存储于block的结构体内部, 会导致block体积变大。特别要注意的是默认情况下block只能访问不能修改局部变量的值。
int age = 10;
myBlock block = ^{
NSLog(@"age = %d", age);
};
age = 18;
block();
输出结果:
age = 10
(2) __block 修饰的外部变量
对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的。block可以修改__block 修饰的外部变量的值。
__block int age = 10;
myBlock block = ^{
NSLog(@"age = %d", age);
};
age = 18;
block();
输出为:
age = 18
为什么使用__block 修饰的外部变量的值就可以被block修改呢?
我们使用 clang 将 OC 代码转换为 C++ 文件:
clang -rewrite-objc 源代码文件名
便可揭开其真正面纱:
__block int val = 10;
转换成
__Block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
};
会发现一个局部变量加上__block修饰符后竟然跟block一样变成了一个__Block_byref_val_0结构体类型的自动变量实例!!!!
weakSelf和strongSelf
类似于c++中的shared_ptr和weak_ptr,用于解决循环引用问题。
1.使用__weak __typeof是在编译的时候,另外创建一个局部变量weak对象来操作self,引用计数不变。block 会将这个局部变量捕获为自己的属性,
访问这个属性,从而达到访问 self 的效果,因为他们的内存地址都是一样的。
2.因为weakSelf和self是两个变量,doSomething有可能就直接对self自身引用计数减到0了.
所以在[weakSelf doSomething]的时候,你很难控制这里self是否就会被释放了.weakSelf只能看着.
3.__strong __typeof在编译的时候,实际是对weakSelf的强引用.
指针连带关系self的引用计数会增加.但是你这个是在block里面,生命周期也只在当前block的作用域.
所以,当这个block结束, strongSelf随之也就被释放了.不会影响block外部的self的生命周期.
总结
在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
如果在 Block 内需要多次 访问 self,则需要使用 strongSelf。
4.property
Objective C中的proprety声明的具体操作如下:
@interface IntervalTimePair : NSObject
@property (nonatomic, copy) NSString *content;
@end
相当于:
@interface IntervalTimePair : NSObject
NSString * _content;
- (void)setContent:(NSString*)c;
- (NSString*)Content;
@end
所以真正的成员变量是_content
,而Content
其实是一个方法。重要!!!
property属性
作用:提供成员变量的访问方法的声明、控制成员变量的访问权限、控制多线程时成员变量的访问环境。
使用范围:property不但可以在interface,在协议protocol和类别category中也可以使用。
@property有两个对应的词,一个是 @synthesize,一个是 @dynamic。如果 @synthesize和 @dynamic都没写,那么默认的就是@syntheszie var = _var;
synthesize 合成访问器方法,表示如果属性没有手动实现setter和getter方法,编译器会自动加上这两个方法。
作用:实现property所声明的方法的定义。
说直白就像是:property声明了一些成员变量的访问方法,synthesize则定义了由property声明的方法。
他们之前的对应关系是:property 声明方法 ->头文件中申明getter和setter方法 synthesize定义方法 -> m文件中实现getter和setter方法。
dynamic 告诉编译器:属性的 setter 与 getter 方法由用户自己实现,不自动生成。假如一个属性被声明为 @dynamic var,而且你没有提供 @setter方法和 @getter 方法,编译的时候没问题,但是当程序运行到 instance.var = someVar,由于缺 setter 方法会导致程序崩溃;或者当运行到 someVar = var 时,由于缺 getter 方法同样会导致崩溃。编译时没问题,运行时才执行相应的方法,这就是所谓的动态绑定。
1、在头文件中:
@property int count;
等效于在头文件中声明2个方法:
- (int)count;
-(void)setCount:(int)newCount;
2、实现文件(.m)中
@synthesize count;
等效于在实现文件(.m)中实现2个方法。
- (int)count
{
return count;
}
-(void)setCount:(int)newCount
{
count = newCount;
}
在Xcode4.5及以后的版本中,可以省略@synthesize,编译器会自动帮你加上get 和 set 方法的实现,并且默认会去访问_age这个成员变量,如果找不到_age这个成员变量,会自动生成一个叫做 _age的私有成员变量。
参考:
iOS底层原理总结 - 探寻OC对象的本质(底层原理系列文章)
OC源码分析之对象的创建(源码分析系列文章)
iOS Block 详解