HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
IOS的ARC会导致的内存泄露问题和解决方案
1.Block的循环引用
循环引用就是当self 拥有一个block的时候,在block 又调用self的方法。这个时候self强引用了block,而在block中使用self也会强引用self。这样就会产生循环引用,导致两个对象都得不到释放。
self.myBlock = ^{
[self doSomething];
};
解决的方法:掐断其中的一条强引用,使之变成弱引用,变成这样,就打破了循环引用:
__weak typeof (self) weakSelf = self;
还有一些系统的Block,这些Block中不用做特殊处理就可以直接使用self,因为这些系统的Block由系统strong引用,我们的代码没有强引用它
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"Self.a %@", self.a);
});
2.NSTimer未释放
在使用 NSTimer addtarget 时,为了防止 target 被释放而导致的程序异常,timer 会强引用 target,所以这也是一处内存泄露的隐患。解决方法是使用线程安全的MSWeakTimer,然后在dealloc中主动调用invalidate
- (void)dealloc {
[timer invalidate];
}
3.performSelector 系列
performSelector分静态调用和动态调用,以下这种调用 selector 的方法和直接调用 selector 基本等效,执行效果相同,不存在内存泄露
[object methodName];
[object performSelector:@selector(methodName)];
还有一种动态绑定方式,编译器不知道即将调用的 selector 是什么,不了解方法签名和返回值,甚至是否有返回值都不懂,所以编译器无法用 ARC 的内存管理规则来判断返回值是否应该释放。因此,ARC 采用了比较谨慎的做法,不添加释放操作,即在方法返回对象时就可能将其持有,从而可能导致内存泄露。在ARC下编译会告警:
warning: performSelector may cause a leak because its selector is unknow [-Warc-performSelector-leak]
-(void)method:(SEL)selector {
[object performSelector:selector];
}
解决方式是使用函数指针,显示的声明这个函数
IMP imp = [viewController methodForSelector:selector];
void (*func)(id, SEL) = (void *)imp;
func(viewController, selector);
4.Delegate循环引用
把delegate声明为strong属性导致了循环引用
@property (nonatomic, strong) SampleViewController *delegate;
解决方法很简单把strong改成weak就行
@property (nonatomic, weak) SampleViewController *delegate;
5.循环未结束
如果某个ViewController中有无限循环,也会导致即使ViewController对应的view关掉了,ViewController也不能被释放。这种问题常发生于animation处理。
CATransition *transition = [CATransition animation];
transition.duration = 0.5;
tansition.repeatCount = HUGE_VALL;
[self.view.layer addAnimation:transition forKey:"myAnimation"];
解决办法是,在ViewController关掉的时候,停止这个animation
-(void)viewWillDisappear:(BOOL)animated {
[self.view.layer removeAllAnimations];
}
6.非OBJC对象
ARC是自动检测OBJC对象的,非objc对象就无能为力了,比如C或C++等。
C语言使用 malloc 开辟,free释放。
C++使用new 开辟,delete释放。
但是在ARC下,不会添加非OBJC对象释放语句,如果没去释放,也会造成内存泄露。
即使用了ARC,我们也要深刻理解iOS的内存管理机制,这样才能有效避免内存泄露。
怎么理解封装和模块化
1.模块划分
做模块化还是要结合实际业务,对目前APP的功能做一个模块划分,在划分模块的时候还需要关注模块之间的层级。
比如说,在我们项目中,模块被分成了3个层级:基础层、中间层、业务层。
-
基础层模块比如像网络框架、持久化、Log、社交化分享这样的模块,这一层的模块我们可以称之为组件,具有很强的可重用性。
-
中间层模块可以有登录模块、网络层、资源模块等,这一层模块有一个特点是它们依赖着基础组件但又没有很强的业务属性,同时业务层对这层模块的依赖是很强的。
-
业务层模块,就是直接和产品需求对应的模块了,比如类似朋友圈、直播、Feeds流这样的业务功能了。
2.代码隔离
模块化首先要做的是代码层面上独立,任意一个基础模块都是可以独立编译的,底层模块绝对不能有对上层模块的代码依赖,还要确保未来也不会再出现这样的代码。
3.依赖管理
选择使用CocoaPods另外一个重要原因就是,可以通过它来管理模块间的依赖,之前项目各个功能之所以难以复用的重要原因之一就是没有声明依赖
缓存问题
数据库底层基于Sqlite。
每个数据库表只有Key, Value两个字段。
直接将JSON数据存储到Value中,并设置Key。
通过Key查找对应Value数据,来进行数据增删改查操作,并更新视图。
1.使用SDWebImage缓存图片。
2.使用YTKKeyValueStore更方便,也可以使用YYCache
3.使用FMDB操作数据库。
这三个必问项:
自我介绍
大概介绍下你的项目
项目中的技术难点和问题以及怎么解决
command + R 之前发生了什么
- 预处理
- 语法和语义分析
- 生成代码和优化
- 汇编
- 链接
main函数里发生了什么
1.系统先读取App的可执行文件( Mach-0文件),从里面获得dyld的路径,然后加载dyld, dyld去初始化运行环境。
2.开启缓存策略,加载程序相关依赖库(其中也包含我们的可执行文件),并对这些库进行链接,最后调用每个依赖库的初始化方法,在这一步,runt ime被初始化。
3.当所有依赖库的初始化后,轮到最后-位(程序可执行文件)进行初始化,在这时 runtime会对项目中所有类进行类机构初始化,然后调用所有的load方法。最后dyld返回main函数地址, main函数被调用,我们便来到程字入口main函数。
App之间跳转实现
-
URL Scheme方式
-
Universal Links方式
虽然在微信内部开网页会禁止所有的Scheme,但是iOS 9.0新增加了一项功能是Universal Links,使用这个功能可以使我们的App通过HTTP链接来启动App。
1.如果安装过App,不管在微信里面http链接还是在Safari浏览器,还是其他第三方浏览器,都可以打开App。
2.如果没有安装过App,就会打开网页。
iOS ipv4和ipv6
- 首先IPv6网络下只能访问IPv6站点,IPv4下只能访问IPv4站点。两者在不经过DNS转换前提下是无法直接互相访问的;
- IPv6网络下通过域名到DNS服务器,然后首先去DNS66查找v6地址,发现没有则继续到DNS44查找v4,找到后返回v4地址,DNS64合成IPv6地址,在经过NAT64地址和协议转换,最终路由到IPv4服务器,最终完成IPv6网络下通过域名访问只有IPv4地址的站点或服务器。
3.ipv4转换成ipv6 如下:
关于ipv6审核被拒绝的问题,公司给的服务器地址是ip地址,不是域名,所以在苹果审核的时候遇到了ipv6的问题
GCDAsyncSocket.h第三方
.m文件
//
// GCDAsyncSocket+GCDAsyncGetIPV6.m
//
//
// Created by xuyushiguang on 2017/1/2.
// Copyright © 2017年 xuyushiguang. All rights reserved.
//
#import "GCDAsyncSocket+GCDAsyncGetIPV6.h"
#define IOS_CELLULAR @"pdp_ip0"
#define IOS_WIFI @"en0"
#define IOS_VPN @"utun0"
#define IP_ADDR_IPv4 @"ipv4"
#define IP_ADDR_IPv6 @"ipv6"
#import <ifaddrs.h>
#import <arpa/inet.h>
#import <net/if.h>
@implementation GCDAsyncSocket (GCDAsyncGetIPV6)
/**
1,用于ip地址ipv4和ipv6之间的转换(如果手机连接的是ipv6的wifi可用这个方法传入ipv4获取ipv6地址)
2,根据传入的ip地址,获取你想要的IP地址,比如手机连接的是ipv6的wifi,你传入的是ipv4的地址则返回一个ipv6的地址,前提是你的手机链接的是外网。
3,怎么判断手机连接的是不是ipv6的Wi-Fi,可根据+(BOOL)isIpv6Net这个方法判断
@param ipAddr 传入的ip地址,主要是传入ipv4的ip地址
@param port 端口号,要和你传入的ip地址的端口号一直
@return 返回和手机连接的Wi-Fi的ip类型相同的ip,比如手机连接的是ipv6的Wi-Fi,则返回一个ipv6的地址
*/
+ (NSString*)getProperIPWithAddress:(NSString*)ipAddr port:(UInt32)port
{
NSError *addresseError = nil;
NSArray *addresseArray = [GCDAsyncSocket lookupHost:ipAddr
port:port
error:&addresseError];
if (addresseError) {
NSLog(@"");
}
NSString *ipv6Addr = @"";
for (NSData *addrData in addresseArray) {
if ([GCDAsyncSocket isIPv6Address:addrData]) {
ipv6Addr = [GCDAsyncSocket hostFromAddress:addrData];
}
}
if (ipv6Addr.length == 0) {
ipv6Addr = ipAddr;
}
return ipv6Addr;
}
//判断是否是ipv6(判断手机连接的是不是ipv6的WiFi)
+ (BOOL)isIpv6Net{
NSArray *searchArray =
@[ IOS_VPN @"/" IP_ADDR_IPv6,
IOS_VPN @"/" IP_ADDR_IPv4,
IOS_WIFI @"/" IP_ADDR_IPv6,
IOS_WIFI @"/" IP_ADDR_IPv4,
IOS_CELLULAR @"/" IP_ADDR_IPv6,
IOS_CELLULAR @"/" IP_ADDR_IPv4 ] ;
NSDictionary *addresses = [self getIPAddresses];
__block BOOL isIpv6 = NO;
[searchArray enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop)
{
if ([key rangeOfString:@"ipv6"].length > 0 && ![[NSString stringWithFormat:@"%@",addresses[key]] hasPrefix:@"(null)"] ) {
if ( ![addresses[key] hasPrefix:@"fe80"]) {
isIpv6 = YES;
}
}
} ];
return isIpv6;
}
//获取手机连接的ip地址(包括ipv4地址和ipv6地址)
+ (NSDictionary *)getIPAddresses
{
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
// retrieve the current interfaces - returns 0 on success
struct ifaddrs *interfaces;
if(!getifaddrs(&interfaces)) {
// Loop through linked list of interfaces
struct ifaddrs *interface;
for(interface=interfaces; interface; interface=interface->ifa_next) {
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
continue; // deeply nested code harder to read
}
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
NSString *type;
if(addr->sin_family == AF_INET) {
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
type = IP_ADDR_IPv4;
// NSLog(@"ipv4 %@",name);
}
} else {
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
type = IP_ADDR_IPv6;
// NSLog(@"ipv6 %@",name);
}
}
if(type) {
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
addresses[key] = [NSString stringWithUTF8String:addrBuf];
}
}
}
// Free memory
freeifaddrs(interfaces);
}
return [addresses count] ? addresses : nil;
}
@end
iOS .m文件总结
-
.m文件是纯Object-C 文件
.mm是Object-C和C++混合文件
-
.m只能调用纯Object-C的类,不能调用混合的
.mm可以调用Object-C的,也可以调用C++的
-
.m要调用混合的怎么办?
可以
解决办法是.h用Object-C的方式,而具体实现用.mm的方式
网友评论