1、Objective-C的类可以多重继承?可以采用多个协议吗?
不可以多重继承,可以采用多个协议。
2、Category是什么?扩展一个类的方式用继承好还是类目好?为什么?
Category是类目。
用类目好,因为继承要满足a is a b的关系,而类目只需要满足a has a b的关系,
局限性更小,你不用定义子类就能扩展一个类的功能,还能将类的定义分开放在不
同的源文件里, 用Category去重写类的
方法,仅对本Category有效,不会影响到其他类与原有类的关系。
3、延展是什么?作用是什么?
延展(extension):在自己类的实现文件中添加类目来声明私有方法。
4、类实例(成员)变量的@protected ,@private,@public声明各有什么含义?
@protected:受保护的,该实例变量只能在该类和其子类内访问,其他类内不能访问。
@private:私有的,该实例变量只能在该类内访问,其他类内不能访问。
@public:共有的,该实例变量谁都可以访问。
@package:包保护的,该实例变量可以被相同框架包中的其他类访问,其他框架包中
的类不可以访问
5、id声明的对象有什么特性?
(1)没有 * 号
(2)动态数据类型
(3)可以指向任何类的对象(设置是nil),而不关心其具体类型
(4)在运行时检查其具体类型
(5)可以对其发送任何(存在的)消息
6、#import与#include区别
(1) #import是一个条件预编译语句,作用是将头文件中的所有源代码原封不动
的置换至当前位置,作用与#include相同
(2)#import相比#include可以防止交叉编译
(3) #include需要与#ifndef、#define、#endif条件预编译语句结合使用
防止交叉编译
(4) 常用的条件预编译语句: #import、#include、、#define、#elif、
#else、#ifndef、#if、#endif
(5) <>导入函数库、框架的头文件,“”导入用户自定义的头文件
(6) <Foundation/Foundation.h> 中Foundation是框架名称,
Foundation.h是框架的“主头文件”,
7、简述const关键字的作用
(1)修饰一般的局部变量,如const int a = 100; 那么a就由变量变成常量,不可以再重新赋值
(2)修饰指针变量,有以下4种方式
<1> const int *
<2>int const * (1)(2)作用相同,变量p的指向可以改变,但是不能通
过p间接改变它指向的内存中的数值
<3>int * const 变量p的指向不可以改变,但是可以通过p间接改变指向内
存中的数值,定义和初始化必须写在一起
<4>const int * const 综合了以上2个的特点
8、iOS回传机制 - 委托代理模式
(1) 把接收数据的类做成代理类(农民)
(2) 把发送数据的类做成委托类(地主)
(3) 委托类 - 定义协议;定义一个assign修饰的,采用了协议的一个名为delegate
的属性;做协议中方法的调用
(4) 代理类 - 协议中的方法的定义/实现
(5) 一对一关系
9、委托和委托方双方的property声明用什么属性?为什么?
(1)委托和委托方双方的property声明属性都是assign而不是retain,为了避免循环
引用造成的内存泄露。
(2)循环引用的问题这样理解:
▪ 比如在main函数中创建了两个类的对象A和B,现在引用计数都是1。现在让
A和B互相引用(A有一个属性是B对象,属性说明是retain;B有一个属性是A
对象,属性说明是retain),现在两个对象的引用计数都增加了1,都变成了2。
▪ 现在执行[A release]; [B release]; 此时创建对象的main函数已经释放了自己
对对象的所有权,但是此时A和B的引用计数都还是1,因为他们互相引用了。
▪ 这时你发现A和B将无法释放,因为要想释放A必须先释放B,在B的dealloc方
法中再释放A。同理,要想释放B必须先释放A,在A的dealloc方法中再释放B。
所以这两个对象将一直存在在内存中而不释放。这就是所谓的循环引用的问题。
(3)要想解决这个问题,一般可以将引用的属性设置为assign,而不是retain来处理。
10、iOS回传机制 - Block块函数
(1)把接收数据的类做 - Block函数的定义
(2)把发送数据的类做 - Block函数的声明和调用
11、iOS回传机制 - 通知
(1)接收数据的类做通知的注册监听
(2)发送数据的类做通知的发送
(3) 注册监听代码 必须 先于发送通知的代码执行!
(4) 可以实现一对多
12、内存管理的几条原则是什么?按照默认法则,哪些关键字生成的对象需要手动释放?哪些情况下不需要手动释放,会直接进入自动释放池?
(1) OC使用了“引用计数”的内存管理机制
(2)当使用new、alloc或copy方法创建一个对象时,该对象引用计数器为1。如果不需要
使用该对象,可以向其发送release或autorelease消息,在其使用完毕时被销毁。
(3)当对象调用retain方法时,引用计数值加1;调用release方法时,引用计数值减1;调
用autorelease方法时,引用计数值不会立即减1,会将对象放入自动释放池,当程序
退出时,自动释放池会将其中的所有对象的计数值都减1; 如果retain了某个对象,需
要release或autorelease该对象,保持retain方法和release方法使用次数相等
(4)当引用计数值为0时,系统自动调用dealloc方法,将对象内存销毁
(5)使用new、alloc、copy关键字生成的对象和retain了的对象需要手动释放,即调用
release方法。设置为autorelease的对象不需要手动释放,会直接进入自动释放池。
(6)使用类方法创建的对象,一般会默认放入自动释放池,不需要手动调用release方法
去释放其内存,如NSArray *arr = [NSArray arrayWithObjects:@“china”,@“beijing”];
此时arr不需要调用release方法,不然可能会出现内存释放再去调用的崩溃错误!
NSString *s = [[NSString alloc] init];
[s release];
NSString *s2 = [[[NSString alloc] init] autorelease];
NSString *s3 = [NSString string];
13、iOS沙盒中三个目录及作用
(1)Document:用户定义的持久化的文件
(2)Library:NSUserDefaults持久化的数据、
持久化的底层数据
(3)Tmp:临时的缓存数据
14、类方法、实例方法
(1)OC中用+表示类方法,只能由类名调用,不可用对象调用!
(2)OC中用-表示实例方法/对象方法,只能由对象调用,不可用类名调用!
(3)成员变量、属性 只能用在实例方法中,不可用在类方法中
(4)类方法中 使用self、super关键字,只能去调用其他的类方法,不可调用实例方法
(5)实例方法中,使用self、super关键字,只能去调用其他的实例方法,不可调用类方法
15、extern关键字
(1)extern+类型+全局变量名称
(2)作用是申明引用一个外部变量/全局变量
(3)不会重新申请新的内存空间,去引用全局变量的内存
16、求斐波那契数列
int finoNum(int month)
{
if(month == 1 || month == 2)
return 1;
return finoNum(month-1)+finoNum(month-2);
}
17、Block函数
/*
---------- Block函数声明 ---------
*/
void (^printConutry_Block)(void);
double (^getPI_Block)();
void (^printArgumentsSum_Block)(int,int,int);
int (^getSum_Block)(int a,int b);
/*
------------ Block函数定义 --------------
*/
void (^printConutry_Block)(void) = ^(void){
printf("中国\n");
};
double (^getPI_Block)() = ^(){
return 3.2;
};
void (^printArgumentsSum_Block)(int,int,int) = ^(int a,int b,int c){
printf("sum=%d\n",a+b+c);
};
int (^getSum_Block)(int a,int b) = ^(int a,int b){
return a+b;
};
/*
------ Block函数调用 --------
*/
printConutry_Block(); // printMyCountry();
printf("pi=%g\n",getPI_Block()); // getPI();
printArgumentsSum_Block(10,20,30);
printf("sum=%d\n",getSum_Block(100,200)); // getMax(33, 44)
(1) Block函数的声明、定义、调用类似于C函数,但是注意写法的不同!
(2) C函数特点: 可以嵌套调用、可以递归调用、不可以嵌套定义、不可以使用函数名赋值
(3) Block函数特点:可以嵌套调用、可以递归调用、可以嵌套定义、可以使用函数名赋值
(4) Block函数的嵌套定义:
int main(){
/*
-- Block函数的嵌套定义 ----
*/
NSString*(^blockInnerTest)(int,NSString*) = ^(int index,NSString *s){
NSString* sub = [s substringFromIndex:index];
return sub;
};
NSString *s = blockInnerTest(2,@"China");
NSLog(@"%@",s);
return 0;
}
(5)假设新的Block函数的参数、实现都与一个已有的Block函数相同,可以使用函数名赋值
/*
Block函数名直接赋值
*/
int (^sum)(int,int) = getSum_Block;
NSLog(@"sum=%d",sum(10,20));
NSLog(@"sum=%d",getSum_Block(10,20));
(6) 在Block函数体中,只能使用函数体外的局部变量的值,不可以改变局部变量的值;
如果想在Block函数体中对外部局部变量赋值,方式有3:
<1> 将外部的局部变量 变成 全局变量
<2> 将局部变量变为__block变量(两个下划线)
<3> 将局部变量变为static静态局部变量
__block int innerNum = 200;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
innerNum = 200;
});
(7) 可以将Block函数做成OC的属性
@property(nonatomic,strong)void (^Pass)(id data);
函数名就是OC对象的属性的名称,使用如: self.Pass...
(8) 可以将Block函数做成一个类型,并定义其变量
typedef void(^blockType)(int,NSString*); // blockType变为一个类型说明符
blockType a = ^(int num,NSString *str) {
………..
};
blockType b ;
blockType c = a; // a、b、c都是blockType类型的变量
17-2、Swift闭包 (等效于OC的Block函数)
(1)————— 闭包定义 —————
// 无参数无返回值的闭包
var blockA:()->Void = {
print("China")
}
// 无参数有返回值
var blockB:()->Double = {
return 3.14
}
// 有参数无返回值
var blockC2:(Int,Int)->Void = {
(x:Int,y:Int)->Void in
print("尹晓腾")
print(x+y)
}
// 有参数有返回值的闭包
var blockD:(Int,Double)->Int = {
(x:Int,y:Double)->Int in
return x * Int(y)
}
(2)——— 闭包定义简化 -------
var blockD:(Int,Double)->Int = {
(x:Int,y:Double)->Int in
return x * Int(y)
}
var blockD2:(Int,Double)->Int = {
(x,y)->Int in
return x * Int(y)
}
var blockD3:(Int,Double)->Int = {
(x,y) in
return x * Int(y)
}
var blockD4:(Int,Double)->Int = {
x,y in
return x * Int(y)
}
var blockD5:(Int,Double)->Int = {
return $0 * Int($1)
}
(3)——— 闭包调用 -------
blockA()
var pi = blockB()
print(pi)
blockC(100,200)
var res = blockD(100,3.14)
print(res)
(4)———— 函数类型作为另一个函数的形参 ————
func test(num:Int,dNum:Double)->Int {
return num + Int(dNum)
}
func showTest(a:Int,b:Double,c:(Int,Double)->Int)->Void{
print(c(a,b))
}
showTest(a: 100, b: 3.14, c: test)
(5)———— 闭包代替同类型的函数类型作为另一个函数的形参 ————
func showTest(a:Int,b:Double,c:(Int,Double)->Int)->Void{
print(c(a,b))
}
showTest(a: 100, b: 3.14, c: blockD)
showTest(a: 100, b: 3.14, c: {
(x:Int,y:Double)->Int in
return x / Int(y)
})
showTest(a: 100, b: 3.14, c: {
x,y in
return x + Int(y)
})
showTest(a: 100, b: 3.14, c: {
return $0 - Int($1)
})
(6)———— 尾随闭包 ————
showTest(a: 100, b: 3.14) { (x, y) -> Int in
return x % Int(y)
}
(7)—— 系统提供的带闭包的方法调用 ——
var arr = [21,3,15,7,32,19]
var sortArr = arr.sorted()
print(sortArr)
// 闭包表达式方式
sortArr = arr.sorted(by: {
x,y in
return x > y
})
// 尾随闭包方式
sortArr = arr.sorted { (x, y) -> Bool in
return x > y
}
sortArr = arr.sorted { x, y in
return x > y
}
sortArr = arr.sorted {
return $0 > $1
}
sortArr = arr.sorted {
$0 > $1
}
18、iOS持久化技术
(1)plist文件持久化 (writeToFile)
(2)sqlite3 - 嵌入式的小型数据库,C函数 (中型MySQL、 SQLServer) (Oracle、DB2 大型)
(3)NSUserDefaults - 参数持久化
(4)CoreData (苹果封装的,以SQLite3底层实现的持久化框架,OC方法
第三方 FMDB (底层也是使用SQLite3实现)
19、pch文件使用
(1)pch是整个工程的条件预编译的头文件
(2)在该文件中定义的符号 可以在工程的每个文件中直接使用 而不用再声明
(3)添加方式 - <1> NewFile - OtherFiles - PCH File
<2> 在工程设置的BuildSettings选项中,找到Apple LLVM Language - PrefixHeader选项,
在右侧空白添加创建的pch文件的路径 - $(SRCROOT)/工程目录名称/pch文件全称
20、NSStringFromSelector(_cmd)
_cmd表示本方法的 @selector/SEL 封装
21、nil、NULL、Nil、NSNull区别
(1)nil表示对象指向空,如:NSString *s = nil;
nil对象调用方法,编译、运行都不崩溃,只是什么都不执行!
(2)NULL表示指针为空,用于C语言的指针,如 int *p = NULL;
(3)Nil 表示类为空
(4)NSNull表示空对象,NSArray *arr = [NSArray arrayWithObjects:@“yin”,@“wang”,nil,@“china”,@100,nil];
nil 在此处表示数组成员的一个结束标志,不是数组的成员,而且数组会以第一个出现的nil作为结束标志!
如果想在数组中添加一个空对象作为数组的成员,必须使用NSNull类,NSNull类只有一个对象的创建方式,
即[NSNull null]
NSArray *arr = [NSArray arrayWithObjects:@"1",@23, [NSNull null],@"china",@100,nil];
22、Plist持久化关于自定义类对象的持久化
(1) NSString、NSArray、NSDictionary自带writeToFile方法,可以直接将字符
串、数组、字典数据写入文件中保存
(2)如果将自定义对象写入文件,或者将自定义对象加到数组或字典中将数组或
字典写入文件,都需要对自定义类做归档操作
(3)OC对自定义类做归档操作,须采用NSCoding协议,实现协议的两个方法:
<1>encodeWithCoder:
<2>initWithCoder:
(4) 对自定义类做归档操作后,还需要使用NSKeyedArchiver序列化类将自定义对
象转换为二进制数据写入文件,使用NSKeyedUnArchiver反序列化类将持久化
的二进制数据读取转换为OC的对象类型
23、SQL语句
(1)DDL语句:(数据库定义语句) create、drop、alter
<1>create语句:
"CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,password TEXT,phone TEXT UNIQUE)"
(2)DML语句: (数据库操作语句)insert、update、delete、select
<1>insert语句:
"INSERT INTO users (name,password,phone) VALUES (?,?,?)”
<2>update语句:
"UPDATE users SET name = ? , password = ? WHERE id = ?”
<3>delete语句:
"DELETE FROM users WHERE id = ?”
<4>select语句:
"SELECT * FROM users"
"SELECT name,password FROM users where id > 5 and id < 10”
“SELECT name \’姓名\’,password \’密码\’ FROM users where phone like \’133\%\’ “
24、单例模式
(1)不管实例化多少个对象,都指向同一块内存空间,这样的类叫单例类
(2)iOS提供的单例类:
[NSFileManager defaultManager]、[UIApplication sharedApplication]、
[UIScreen mainScreen]、[UIDevice currentDevice]、
[NSNotificationCenter defaultCenter]、
[NSUserDefaults standardUserDefaults]
(3)怎样自定义单例类:
<1> 在类的.m文件中定义一个static修饰的本类的全局对象,并赋值为nil
<2>在类的.h文件中声明一个类方法,以让外部类获取唯一的单例对象内存
<3>在类的.m文件中定义.h中声明的类方法,使用懒加载方式创建单例对象内存
<4>在类的.m文件中重写allocWIthZone:方法,使用懒加载方式实现代码,保证
使用alloc方法创建对象时,只得到一块内存
<5>采用NSCopying协议,实现协议中的copyWithZone:方法,使用懒加载方式
实现代码,防止对象调用copy方法时生成新的内存空间
<6>采用NSMutableCopying协议,实现协议中的mutableCopyWithZone:方法,
使用懒加载方式实现代码,防止对象调用mutableCopy方法时生成新的内存空间
<7>在MRC环境下,重写retain和release方法,防止内存计数的改变,让单例类内存
的计数值永远是1,只有程序退出时将内存计数值减1然后销毁
25、instancetype与id的区别?
(1)instancetype可以用在任何类中,代表所在类的对象类型
(2)id可以表示任意类的对象类型
(3)instancetype一般只能用作方法的返回值类型,不可以用作方法的参数类型,
不可在方法内部使用它作为类型说明符定义对象
(4)id既可以作为方法返回值类型,也可作为方法参数类型,也可在方法内部使用
它作为类型说明符定义对象
(5)id类型的对象可以调用所有类中定义的任何一个公开的方法,编译都成功!但是
运行可能会崩溃,要去注意id对象的运行类型是什么!
(6)NSObject类提供了判断id对象运行类型的两个方法:
<1> isKindOfClass: - 判断是不是某个类或其子类的对象类型
<2>isMemberOfClass: - 判断是不是某个类(不包含其子类)的对象类型
(7)id类型对象不可以调用类中的属性,如果想调用,必须将id对象强制装化为某个类的
类型,如:
id obj = [[Student alloc] init];
obj.name = @“尹晓腾”; // 调用编译报错
Student *s = (Student *)obj; // 必须强制转化
s.name = @“尹晓腾”; // 才可以调取类中的属性
((Student *)obj).name = @“王梅”; // OK!
26、深拷贝、浅拷贝区别?
(1)对于Foundation框架提供的类,比如字符串、数组、字典,实现浅拷贝代码如下:
NSArray *arr = @[@“china”];
NSArray* arr2 = arr; // <1> 直接用对象名赋值
NSArray* arr3 = [arr copy]; // <2> 不可变对象调用copy
“浅拷贝”实现的是对象“地址”的复制,两个对象都指向同一块内存空间;
其中一个对象销毁,另一个对象就不能再使用;
对于可变数组或字符串,一个对象改变内容,另一个对象的内容也跟着改变。
(2)对于Foundation框架提供的类,比如字符串、数组、字典,
实现深拷贝代码如下:
NSArray* arr = @[@“china”];
NSMutableArray *arr3 = [arr mutableCopy]; // <1>不可变对象调用mutableCopy
NSArray* arr4 = [arr3 copy]; // <2>可变对象调用copy方法
“深拷贝”实现的是对象“内容”的复制,两个对象都指向两个不同的内存空间,但是内存中的内容相同;
其中一个对象销毁,另一个对象不受影响,可以使用;
对于可变数组或字符串,一个对象改变内容,另一个对象的内容不会跟着改变。
(3)注意copy和mutableCopy的使用!
<1> 不可变对象调用copy实现的是浅拷贝
<2> 可变对象调用copy、mutableCopy;不可变对象调用mutableCopy实现深拷贝
<3> 如果生成可变对象时错调用了copy方法,如下:
NSArray* arr = @[@“china”];
NSMutableArray *arr3 = [arr copy]; // 应改为mutableCopy
那么arr3调用NSArray的方法,调用NSMutableArray的方法,编译都成功;
但是运行NSArray方法成功,运行NSMutableArray方法时会崩溃
<4>如果生成不可变对象时错调用了mutableCopy方法,如下:
NSArray* arr1 = @[@“china”];
NSMutableArray *arr2 = [arr1 mutableCopy];
NSArray* arr3 = [arr2 mutableCopy]; // 注意!
那么arr3不能调用NSMutableArray的方法,编译报错;
arr3可以调用NSArray的方法,编译、运行都成功!
但如果写为如下方式:
NSArray* arr1 = @[@“china”];
NSMutableArray *arr2 = [arr1 mutableCopy];
id arr3 = [arr2 mutableCopy];
那么arr3既可以调用NSArray方法,又可以调用NSMutableArray方法,
编译、运行都成功!
<5> @property(nontomic,copy)NSMutableString *str;
self.str = [[NSMutableString alloc] init];
[self.str appendString:@“china”]; // 出错,运行崩溃
[self.str stringByAppendingString:@“china”];
(4)自定义对象实现深拷贝
<1> 自定义对象调用copy方法,如:
Student *s = [[Student alloc] init];
Student *s2 = [s copy];
<2>使用copy方法,
A:必须在自定义类中重写-(id)copy;方法
B: 采用NSCopying协议,实现协议中的copyWithZone:方法
<3>使用第<2>步中的两个方法,可以调用copy,但不一定就是深复制,关键看代码如何去写!
(5)
<1> 不可变对象 调用 copy 实现“浅拷贝”,得到一块“不可变”的内存
<2> 不可变对象 调用 mutableCopy 实现“深拷贝”,得到一块“可变”的新内存
<3> 可变对象 调用copy 实现 “深拷贝” ,得到一块“不可变”的新内存
<4> 可变对象 调用mutableCopy 实现 “深拷贝” ,得到一块“可变”的新内存
27、self和super的区别?
(1)self是类的隐藏的参数变量,用在方法(类方法、实例方法)内部,表示调用该方法的类或对象
(2)super不是类的隐藏的参数,只是一个“编译器指示符”,表示从调用该方法的类或对象的父类中
去查找对应的属性或方法
(3)self与super都指向同一个对象,只是查找方法的位置不同,一个从本类中,一个从本类的超类中
(4)假设类Student继承自Human,在子类中有代码如下
NSLog(@"%@",NSStringFromClass([self class]));
NSLog(@"%@",NSStringFromClass([super class]));
结果都是Student
(5)self调用方法在runtime机制中调的函数是objc_msgSend(id self,SEL _cmd,…);
super调用方法在runtime机制中调用函数是objc_msgSendSuper(struct objc_super *super,SEL _op…);
struct objc_super{
id receiver;
Class superClass;
};
LoginRegister - 登录注册
News - 新闻
Weather - 天气预报
Codes - Ticket - 票务查询
Personal - 个人信息
Services - 业务类
Category - 类别
Videos - 视频
Soruce - Images - 图片
Music - 音乐
Files - 其他文件
Third - 第三方文件
28、pt、px区别?
(1)px表示像素值
(2) pt表示开发点
29、NSObjcRunTime方法?
(1)NSString *NSStringFromClass(Class aClass);
(2) Class _Nullable NSClassFromString(NSString *aClassName);
30、iOS获取App当前的版本号代码
NSString *currentVersion = [[NSBundle mainBundle].infoDictionary objectForKey:@"CFBundleShortVersionString"];
31、导航条与UIScrollView冲突解决方法
(1)在添加UIScrollView的ViewController中添加代码
self.edgesForExtendedLayout = UIRectEdgeNone;
(2)在添加UIScrollView的ViewController中添加代码
self.automaticallyAdjustsScrollViewInsets = YES;
(3)如果以上两种方法都不可以解决,使用下面方法:
在添加UIScrollView的ViewController中添加代码:
UIView *v = [[UIView alloc] init];
[self.view addSubview:v];
然后再正常添加UIScrollView即可
32、响应者链(Responder Chain)
(1)响应者对象(Responder Object)指的是有响应和处理“事件/消息/方法”能力的对象
(2)响应者链就是由一系列的响应者对象构成的一个层次结构
(3)UIResponder是所有响应对象的基类。我们常用的UIApplication、UIViewController、
UIWindow和所有继承自UIView的类都直接或间接继承自UIResponder
(4)响应者链事件传递过程:
<1> 一般view是响应者链中事件的第一响应者
<2> view不响应事件,如果view是控制器的view,就传递给控制器,如果不是,就传递给view的父视图
<3> 父视图同样按照第<2>步的方式响应事件,直至将事件传递给视图层次结构的最顶级视图
<4> 如果顶级视图也不响应事件,将事件传递给window对象处理
<5> window对象不处理的话,传递给UIApplication对象处理
<6> UIApplication对象也不处理,事件将被丢弃
(5) 实际应用,View与Controller分离,在View中获得所在的Controller对象
-(UIViewController *)viewController {
// 使用响应者链的方式,获得self所在的Controller对象
for (UIView *preView = self; preView; preView = self.superview) {
UIResponder *next = preView.nextResponder;
if ([next isKindOfClass:[UIViewController class]]) {
return (UIViewController *)next;
}
}
return nil;
}
33、BOOL、boolean
(1)C语言是没有布尔类型,用0值表示假,用非0值表示真
int a = -10;
if(a){ } 表示逻辑为真!
if(50<a<100) { } // 可以如此书写,编译OK,但是不管a为什么数值,条件永为真!
(2)C++、Java中使用boolean表示布尔类型,数值包括true、false
Java中不支持用0值表示假,用非0值表示真的写法!
int a=10;
if(a) { // Java中如此书写编译报错!
}
if(50<a<100) { } // 不可以如此书写,编译错误!
(3)OC中使用BOOL表示布尔类型,数值包括YES、NO;
因为OC是从C扩展而来,在OC中,也可以使用0值表示假,用非0值表示真
34、Mac终端命令(Unix、Linux命令)
(1)/ 系统根目录
(2)~ 用户根目录
(3). 当前目录
(4).. 上一级目录
(5)cd 切换目录
(6)ls(-a) 展示当前目录中所有的文件及子目录
(7)mkdir 创建新目录
(8)rm -rf 删除目录
(9)rm 删除文件
35、%@与-(NSString *)description方法
(1)任意一个Foundation的OC的对象都可以使用%@执行打印操作
(2)Foundation中的类的对象使用%@打印可以显示内容
(3)自定义对象使用%@打印,出现 <类名:0xfffffff> 的内容,显示的是类名和对象的地址
(4)如果想用%@打印自定义对象时能显示对象的属性信息,须在自定义类中重写-(NSString *)description方法
36、获取沙盒中的Documents目录路径的三种代码方式
// (1)
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
// (2)
NSString *documentPath2 = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
// (3)
NSURL *documentURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
37、获取某个指定范围的随机数
假设要获取M到N的随机数(M<N),可套用以下公式:
int ranNum = M + arc4random() % (N-M+1);
假设取出33到69的随机数
33 + arc4random() % (69-33+1);
38、iOS获取键盘高度
(1)注册监听,监听键盘的弹出
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleKeyBoardNotification:)
name:UIKeyboardWillShowNotification
object:nil];
(2)在监听的触发方法当中,使用如下代码获取键盘高度
-(void)handleKeyBoardNotification:(NSNotification *)sender {
// 获取键盘高度,关键的一句
NSValue *value = [sender.userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"];
CGSize keyboardSize = [value CGRectValue].size;
float keyboardHeight = keyboardSize.height;
}
39、属性修饰符
strong:强引用,会将参数对象的“引用计数”值加1,一般用来修饰OC的对象类型,
不可以修饰基本类型!用在ARC环境下
@property(nonatomic,assign)NSString *s;
// (MRC环境)用assign修饰s,调用以下两段代码,程序可能会崩溃
// 用strong修饰s,就OK
-(void)test {
self.s = [[NSString alloc] init];
[self.s release];
}
-(void)sss {
NSLog(@“%@”,self.s);
}
retain: 强引用,同上,一般用在MRC环境下
assign:弱引用,对参数对象的“引用计数”值不做任何修改,一般修饰基本类型,
但是也可以修饰OC的对象类型,在委托代理模式中,
修饰委托类的id delegate属性一般用assign,防止循环引用(retain cycle)
weak:弱引用
copy:复制,对参数对象做复制操作,可能是深复制,也可能是浅复制,参考26
@property(nonatomic,copy)NSString *s;
NSString *name = @“China”;
self.s = name; // 因为参数是一个不可变对象,不可变对象调用copy实现浅拷贝
[self.s stringByAppendingString:@“Beijing”]; // 编译、运行都OK!
[self.s appendString:@“Beijing”]; // 编译报错!
NSMutableString *country = [NSMutableString stringWithUTF8String:”Beijing”];
self.s = country; // 因为参数是可变对象,可变对象调用copy实现深拷贝
[country appendString:@“Bawei”];
NSLog(@“%@”,self.s); // Beijing
NSLog(@“%@”,country); // BeijingBawei
[self.s stringByAppendingString:@“Beijing”]; // 编译、运行都OK!
[self.s appendString:@“Beijing”]; // 编译报错!
————————————————————————————————
@property(nonatomic,copy)NSMutableString *s;
NSString *name = @“China”;
self.s = name; // 因为参数是一个不可变对象,不可变对象调用copy实现浅拷贝
[self.s stringByAppendingString:@“Beijing”]; // 编译,运行都OK!
[self.s appendString:@“Beijing”]; // 编译OK,运行崩溃
NSMutableString *country = [NSMutableString stringWithUTF8String:”Beijing”];
self.s = country; // 因为参数是可变对象,可变对象调用copy实现深拷贝
[country appendString:@“Bawei”];
NSLog(@“%@”,self.s); // 内容还是“Beijing”
NSLog(@“%@”,country); // 内容是“BeijingBawei”
[self.s stringByAppendingString:@“Beijing”]; // 编译,运行都OK!
[self.s appendString:@“Beijing”]; // 编译OK,运行崩溃
nonatomic:非原子性操作,多线程不安全的,一般选择nonatomic修饰属性,在非多线程开发中提高性能
atomic:原子性操作,多线程安全的(如果在多线程开发中,多个线程同时对一个可变对象做修改,可以使用该修饰符)
readwrite:可读写的,生成setter方法和getter方法
readonly:只读的,生成getter方法,没有setter方法
40、 #include<stdio.h>
(1)标准输入输出函数库
(2)printf()、scanf()、fgetc()、fputc()、fgets()、fputs()、fread()、fwrite()、fprintf()、fscanf()
#include<string.h> C语言字符串操作函数库
strcpy() 字符串拷贝
strcmp() 字符串比较
#include<math.h> // 数学函数库
#include<windows.h> //
41、OC语言的优缺点
(1)优点:
<1>
<2>
(2) 缺点:
<1> 不可以做类型安全判断
42、C、OC语言数据占位符
(1)整数字面常量
int num = 35; // 35表示十进制数 35
int num = 035; // 35表示八进制数35
int num = 0x35; // 35表示十六进制数35
(2) 整形占位符
<1> %d : 十进制方式
<2>%+-md : 十进制方式打印,但是如果m的绝对值小于等于数字实际占用列数,原样输出;
如果m的绝对值大于实际列数,正数(+)左补空格凑成m列打印,
负数右补空格凑成m列
printf(“A%4dZ”,12); // A 12Z(A和12之间有2个空格)
printf(“A%-4dZ”,12); // A12 Z(12和Z之间有2个空格)
printf(“A%4dZ”,1314); // A1314Z
<3>%0md : 如果m的值小于等于数字实际占用列数,原样输出;
如果m的值大于实际列数,左补0凑成m列打印
printf(“%04d”,12); // 0012
printf(“%04d”,1314); // 1314
<4> %o : 八进制方式打印
<5> %x : 十六进制打印
<6> %u : 无符号整型(unsigned int) 、无符号短整型(unsigned short)打印方式
<7> %lu: 无符号长整型数(unsigned long)打印方式
<8> 二进制、八进制、十六进制 转为 十进制
以0开始从最后一个数字向前排序,让序号上的数字乘以2/8/16的对应的序号次方,相加
<9> 十进制转换为二进制、八进制、十六进制
对十进制数字辗转除2/8/16取余数,将余数倒置
(3)浮点型
<1> %lf - double类型
<2>%f - float类型
<3>%g - 可以把小数点后面无用的0去掉不显示!
<4>%+-m.nlf - 占位列数打印浮点型数据
m表示打印的数字占用多少列,如果实际列数大于等于m,原样输出;如小于m,为正数左补空格补全,
为负数,右补空格补全!
n表示小数点后保留几位
printf("%lf\n",3.14); // 3.140000
printf("%9lf\n",3.14); // _3.140000 (前面有1个空格)
printf("%-9lfAAAA\n",3.14); // 3.140000_AAAA
printf("%9.2lf\n",3.148); // _____3.15(前面有5个空格,进行四舍五入!!!)
printf("%-9.2lfAAAA\n”,3.148); // 3.15_____AAAA(中间有5个空格,进行四舍五入!!!)
(4)字符型
%c
(5)字符串型
<1> %s
43、C语言字符串表示方式及使用
(1)字符数组存储字符串
char name[100] = “China”;
name = “Beijing”; // 编译错误!!在C当中,数组名表示的是一个地址常量符号,代表数组首元素的地址!
// 此处的name等价于&name[0] ,但是是一个常量符号!
char name[100] = “China\0Beijing\0”;
printf(“%s”,name) ; // 打印结果为China
<1> 在C当中,数组名表示的是一个地址常量符号,代表数组首元素的地址
<2> ‘0’与‘\0’ 不同,‘\0’表示字符串结束标志,打印时并不显示;’0’就是一个一般的字符,打印时显示0
‘0’ASCII码值为48
‘\0’ASCII码值为0
<3> 如果想对name数组重新赋值,必须使用字符串操作函数
strcpy(name, "Ba");
<4>如果比较两个字符串内容是否相同,使用C的字符串操作函数strcmp
if (strcmp(name, "Ba")==0) {
printf("想等");
}
else{
printf("不等!");
}
<5>char name[100] = “China”; printf(“%ld”,sizeof(name)); // 结果是100
char name[] = “China”; printf(“%ld”,sizeof(name)); // 结果是6
(2)字符指针指向字符串
char *name = “beijing”;
name = “China”;
printf(“%s\n”,name);
strcpy(name,”bawei”) ; // 编译OK,运行崩溃!
printf(“%ld”,sizeof(name)); // 不管内容如何变化,永远是8!
<1> 在同一环境下,int*、double*、float*、char*占用内存大小都是相等的!!
44、需要记住的ASCII码值
(1) ’0’ - 48
(2) ‘A’ - 65
(3) ’a’ - 97
(4) ASCII码值为0的三种表示:
<1> 整数0
<2> 字符串结束标志’\0’
<3> NULL
45、Swift整型数据表示范围
(1) Int8 -2^7 ~ 2^7-1 -128 ~ 127
(2) Int16 -2^15 ~ 2^15-1 -32768 ~ 32767
(3) Int32 -2^31 ~ 2^31-1 -2147483648 ~ 2147483647
(4) Int64 -2^63 ~ 2^63-1 -9223372036854775808 ~ 9223372036854775807
(5) UInt8 0 ~ 2^8-1 0 ~ 255
(6) UInt16 0 ~ 2^16-1 0 ~
46、整型数据在计算机内存中的存储
(1)整数在计算机中以补码方式存储
(2)正数的符号位为0,补码与原码相同
(3)负数的符号位为1,补码获得需要经过 原码 - 反码 - 补码
正数15 Int8 num = 15
原码: 00001111 (标红的为符号位,0表示正数)
内存存储 : 补码: 00001111
负数-15 Int8 num = -15
原码 :10001111
反码: 11110000
内存存储 : 补码: 11110001
内存中数据: 11111111
数值: -1
47、Swift类型推断及类型安全
(1)var num:Double = 3.14
num = 100 // OK!
(2) var num:Int = 100
num = 3.14 // 报错!
(3) 不同类型的变量或常量相互赋值,必须做类型转换!不能直接赋值
var num:Int8 = 100
var x:Int16 = 100
num = x // 报错! 改为num = Int8(x)
x = num // 报错! 改为x = Int16(num)
var y:Double = 3.14
y = num // 报错 改为 y = Double(num)
num = y // 报错! 改为num = Int8(y) 结果得到3
48、Swift十六进制浮点型表示方式
(1)0xfp2 15*2^2 = 60
(2) 0xfp-2 15*2^-2 = 3.75
(3) 0xf.3p0 (15+3/16)*(2^0) = 15.1875
(4) 0xf.32p0 (15+3/16+2/16/16) * (2^0) = 15.1953125
49、C、OC、Swift中的转义字符
(1)“\0” 字符串结束标志,空
(2) “\n” 换行
(3) “\t” 水平制表符
(4) “\r” 回车符
(5)”\\” 1个\
(6)“\’” 1个’
(7)”\”” 1个”
(8)”\u{1f4b}” 1个Unicode字符
50、自定义对象放入NSSet集合需要做的操作
(1)因为NSSet集合要求放入其中的元素不重复
(2) 自定义对象判断重复默认是根据对象的地址来判断,
(3)地址相同的对象不会重复放入,地址不同但内容相同的对象也都会放入集合中
(4)为了将内容相同的自定义对象不重复放入集合中,须做以下两个操作
在自定义类中重写以下两个方法:
<1>
-(BOOL)isEqual:(id)object {
// 如果object不是本类或本类的子类的对象,直接返回NO表示内容不相同!
if (![object isKindOfClass:[Human class]]) {
return NO;
}
// 如果object是本类或本类子类的对象,比较所有的属性是不是相同的,都相同返回YES,否则返回NO
Human *obj = (Human *)object;
return [self.name isEqualToString:obj.name] && self.age==obj.age &&
[self.address isEqualToString:obj.address];
}
<2>
// 重写哈希值方法,所有属性的哈希值相加,将其中某一个乘以较大质数,用来判断两个对象相不相同
-(NSUInteger)hash {
return 67 * [self.name hash] + self.age + [self.address hash];
}
(5)这两个方法是NSSet判断集合中元素是否相等的方法,如果调用isEuqal方法为YES并且比较的两个对象的哈希值相等
那么NSSet就认为比较的两个对象相同,不能重复放入集合中;否则认为不同,可以都放入集合中
51、sizeof、strlen对于C字符串数组求值
(1)
char city[100] = “China”;
printf("%ld\n",sizeof(city)); // 100,方括号中的数字就是内存划分的数组大小,sizeof(char) * 100 = 100
printf("%ld\n",strlen(city)); // 5
city[7] = ‘V’ ; // 可以赋值
printf(“%s\n”,city) ; // 结果还是China 内存存储为 ‘C’ ’h’ ‘i’ ’n’ ‘a’ ‘\0’ ‘\0’ ‘V’ 打印遇到第一个‘\0’就停止!
(2)
char city[] = “China”;
printf("%ld\n",sizeof(city)); // 6,方括号中没有数字系统根据初始化的字符串的总个数加1作为数组的长度
printf("%ld\n",strlen(city)); // 5
city[7] = ‘V’; // 数组长度就是6,下标必须在0-5之间,7已经越界
printf(“%s\n”,city) ; // 崩溃!
52、Swift字符与ASCII数值相互转换
(1)将字符转为整数值
var a:Character = "A"
var str = String(a)
var number:UInt32 = 0
for code in str.unicodeScalars {
number = code.value;
}
print(number)
(2)将整数转换为字符
var num = 97
var ch:Character = Character(UnicodeScalar(num)!)
print(ch)
53、 把一个字符串中的字符变换,大小字母变为小写字母,小写字母变为大写字母,其他的不变
(1)C、OC代码实现
char city[] = "cHina1314BeiJing";
printf("%s\n",city);
for (int i = 0; i < strlen(city); i ++) {
if ( city[i] >= 'a' && city[i] <= 'z' ) {
city[i] -= 32;
}
else if (city[i] >= 'A' && city[i] <= 'Z'){
city[i] += 32;
}
}
printf("%s\n",city);
(2)Swift实现
/*
函数,将大写字母变小写,小写字母变大写,其他字符不变
*/
func changeCharacter(chNum:Character) -> Character {
/*
将字符转换为整数
*/
var chStr = String(chNum) // 将字符转为字符串
var num:UInt32 = 0 // 用于接收字符整数值的变量
for item in chStr.unicodeScalars {
num = item.value // 循环只执行一次,获取字符的整数值
}
/*
如果是大小写字母,转换数值
*/
// 如果是大写字母,转换为小写
if num >= 65 && num <= 90 {
num += 32
}
// 如果是小写字母,转换为大写
else if num >= 97 && num <= 122 {
num -= 32
}
/*
将整数转换为字符
*/
var newChNum = Character(UnicodeScalar(num)!)
return newChNum
}
/*
函数调用改变字符串
*/
var string = "china#488BeiJIng" // 测试的字符串
print(string)
var i = 0 // 表示偏移量(循环变量初始值)
while i < string.characters.count { // 循环条件,包含循环变量的终止值
var ch = string[string.index(string.startIndex, offsetBy: i)]
string.replaceSubrange(string.index(string.startIndex, offsetBy:
i)...string.index(string.startIndex, offsetBy: i),
with: String(changeCharacter(chNum: ch)))
i += 1 // 循环变量值变化
}
print(string)
54、Swift中以二进制、八进制、十六进制方式打印整数
/*
将一个整数用二进制方式打印
*/
func binaryPrintIntNumber(num:Int) {
var remainderArr:[Int] = [] // int数组,存储余数
var quotient:Int = num // 表示商的变量,初始值是num
while quotient > 0 { // 商的终值是0
let remainderNum = quotient % 2 // 获取余数值
remainderArr.insert(remainderNum, at: 0) // 插入数组中
quotient /= 2 // 商变化
}
for item in remainderArr {
print(item, terminator: "")
}
print("")
}
/*
将一个整数用八进制方式打印
*/
func octalPrintIntNumber(num:Int) {
var remainderArr:[Int] = [] // int数组,存储余数
var quotient:Int = num // 表示商的变量,初始值是num
while quotient > 0 { // 商的终值是0
let remainderNum = quotient % 8 // 获取余数值
remainderArr.insert(remainderNum, at: 0) // 插入数组中
quotient /= 8 // 商变化
}
for item in remainderArr {
print(item, terminator: "")
}
print("")
}
55、OC数组排序
(1)自带的compare:方法排序(如果数组元素是字符串,按照字符串的ASCII值排序;如果是NSNumber,按照数值排序)
NSArray *names = @[@"China",@"Beijing",@"Tianjin",@"SHanghai"];
NSLog(@"%@",[names sortedArrayUsingSelector:@selector(compare:)]);
结果:Beijing,China,SHanghai,Tianjin
(2)一些实际需求中不能用compare:排序,需要自己制定排序算法
NSArray *arr = @[@"15",@"23",@"45",@"3",@"123"];
NSArray *sortedArr = [arr sortedArrayUsingSelector:@selector(compare:)];
NSLog(@"%@",sortedArr);
结果:123,15,23,3 , 45
// 如果想按照字符串的数值来排序,需要自己制定排序算法,代码实现如下:
sortedArr = [arr sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 intValue] > [obj2 intValue];
}];
NSLog(@"%@",sortedArr);
结果:3,15,23,345,123
56、Swift构建单例类
class DataBase {
static var defaultDB:DataBase? = nil
var name = ""
private init() {
}
static func shared() -> DataBase {
if defaultDB == nil {
defaultDB = DataBase()
}
return defaultDB!
}
}
57、Swift使用++、—-运算符
(1)Swift的Int类型不再支持自增减运算符比如,++a,a++, —a, a—
的方式
(2)如果想让Swift支持这种语法,须重载运算符
extension Int{
// 前++
static prefix func ++(num:inout Int) -> Int {
num += 1
return num
}
// 后++
static postfix func ++(num:inout Int) -> Int {
let temp = num
num += 1
return temp
}
// 前—
static prefix func --(num:inout Int) -> Int {
num -= 1
return num
}
// 后—
static postfix func --(num:inout Int) -> Int {
let temp = num
num -= 1
return temp
}
}
58、OC的runtime
(1)runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API。
(2)在我们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工作者
(3)比如说,下面一个创建对象的方法中, 举例:
OC : [[MJPerson alloc] init]
runtime : objc_msgSend(objc_msgSend("MJPerson" , "alloc"), "init")
(4)用处:
<1> 在程序运行过程中, 动态创建一个类(比如KVO的底层实现)
Class MyClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
<2> 在程序运行过程中, 动态地为某个类添加属性, 修改属性值
BOOL isSuccess = class_addIvar(MyClass, "test", sizeof(NSString *), 0, "@");
参数1: 类名
参数2: 属性名
参数3: 开辟属性占用内存大小(单位为字节)
参数4:对齐方式
参数5:参数类型,“@”官方解释为对象、静态类型或id类型,其他类型参考官网
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ ocrtTypeEncodings.html
返回值:BOOL,是否添加成功
<3> 在程序运行过程中, 动态地为某个类添加方法
(A)
class_addMethod(MyClass, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, “V@:”);
参数1: 类名
参数2: SEL方法名
参数3: IMP指针(IMP是implementation缩写),指向一个函数的指针,每个方法都有一个对应的IMP
参数4: 方法的参数,
若为“i@:@”表示返回值为int,第一个参数为id类型,:表示@selector(_cmd),第2个参数为id类型;
若为“v@:”表示返回值为void,第一个参数为id类型,:表示@selector(_cmd)
(B)然后定义函数
static void addMethodForMyClass(id self, SEL _cmd, NSString *test) {
// 获取类中指定名称实例成员变量的信息
Ivar ivar = class_getInstanceVariable([self class], "test");
// 返回名为test的ivar变量的值
id obj = object_getIvar(self, ivar);
NSLog(@"%@",obj);
NSLog(@"addMethodForMyClass:参数:%@",test);
NSLog(@"ClassName:%@",NSStringFromClass([self class]));
}
(C)定义OC的方法
//这个方法实际上没有被调用,但是必须实现否则不会调用addMethodForMyClass()方法
- (void)addMethodForMyClass:(NSString *)string {
}
<5> 实例化类对象
OC: id myObjc = [[MyClass alloc] init];
runtime:id myobjc = objc_msgSend(MyClass, @selector(alloc));
myobjc = objc_msgSend(myobjc, @selector(init));
<6> 给动态添加的属性赋值
// 通过KVC的方式给myObj对象的test属性赋值
[myobjc setValue:@“wang” forKey:@"test"];
<7> 调用动态添加的方法
// 如果不调用- (void)addMethodForMyClass:(NSString )string 这个方法,
就不会调用static void addMethodForMyClass(id self, SEL _cmd, NSString test) 函数
[myobjc addMethodForMyClass:@"参数"];
<8> 遍历一个类的所有成员变量(属性)\所有方法 (在归档中使用很方便)
- (void)encodeWithCoder:(NSCoder *)encoder {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([PYPerson class], &count);
for (int i = 0; i<count; i++) {
// 取出i位置对应的成员变量
Ivar ivar = ivars[i];
// 获取成员变量的名称的C字符串
const char * name = ivar_getName(ivar);
// 归档
NSString *key = [NSString stringWithUTF8String:name];
id value = [self valueForKey:key];
[encoder encodeObject:value ForKey:key];
}
free(ivars);
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init]) {
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([PYPerson class], &count);
for(int i=0;i<count;i++) {
// 通过下标取出成员变量
Ivar ivar = ivars[i];
// 获取成员变量的名称的C字符串
const char * name = ivar_getName(ivar);
// 归档
NSString *key = [NSString stringWithUTF8String:name];
id value = [aDecoder decodeObjectForKey:key];
[self setValue:value forKey:key];
}
free(ivars);
}
return self;
}
网友评论