第四周
- 最好不用button自己的imageView,直接在button内setImage就行了。
- 懒加载
必须由self.才能对属性的get和set方法进行间接调用,下划线_是直接对实例变量进行操作。因此要想实现懒加载,必须self.属性,否则不会去调get方法。需要注意的是,懒加载在加载时必须判空,并且在判空的时候必须使用下划线_,很显然如果还用self.的话又会不断调get方法导致陷入死循环。 - 可以通过这样小括号中大括号({ })来设置对象的一些属性:
self.amountDetailsKeyLabel.frame = ({
CGRect frame = self.amountDetailsKeyLabel.frame; //生成frame变量
frame.origin.x = xOffset;
frame.origin.y = CGRectGetMaxY(self.moneyKeyLabel.frame) + 10;
frame; //返回frame
});
- imageView contentMode属性
没有带Scale的,当图片尺寸超过ImageView尺寸时,只有部分显示在ImageView中。
UIViewContentModeScaleToFill属性会导致图片变形。
UIViewContentModeScaleAspectFit会保证图片比例不变,而且全部显示在ImageView中,这意味着ImageView会有部分空白。
UIViewContentModeScaleAspectFill也会证图片比例不变,但是是填充整个ImageView的,可能只有部分图片显示出来。
第一阶段总结
在ios岗实习已满一个月,从第一次接触mac os、objective c零基础,到现在可以做一些小的需求,虽然知识储备和经验还远远不够,但是这一个月还是有大量的知识值得总结和回顾。
在第一个阶段的工作学习中我基本完成了制定的学习目标。现阶段从入门oc语言到完成小的业务需求,基本熟悉了ios开发环境,学习使用了各类开发工具和oc基础及其特性。
开发工具
Xcode
Xcode运行于Mac操作系统下,是苹果公司向开发人员提供的集成开发环境(非开源),用于开发Mac OS X,iOS的应用程序。OC或是Swift都可以在Xcode比较简单和轻松的编写。
其中我也学到了许多调试技巧,例如在断点处增添代码,在运行输出栏中也可以使用P、PO等命令来打印想要调试的对象或数据,分屏操作和查看当前ui结构功能也十分实用。
-
在断点处添加代码
-
选择Debugger Command也可以执行p、po命令
-
点亮此处可以看见修改过的文件
-
在输出栏中使用po命令可以print object,便于调试
-
这样可以快速看见当前想找的View所在的viewController,但是要注意该viewController是否被复用,可以通过全局搜索来加以判断
Charles
是一个HTTP代理服务器,HTTP监视器,反转代理服务器,当浏览器连接Charles的代理访问互联网时,Charles可以监控浏览器发送和接收的所有数据。它允许一个开发者查看所有连接互联网的HTTP通信(如果需要查看HTTPS通信也只需安装证书)。将Charles抓取的数据包mock下来,修改mock到的数据就可以设置数据调试代码了。
mock数据的方法示例:
- 首先要选择出想要mock数据的接口,设置断点
- 然后需要再次触发访问该接口。Charles会停留在断点接口,提供一个Edit Requese页,可提供修改参数以及请求类型、请求链接的校验。
- 接着点击Execute进行执行下一步,选择JSON Text可以看到断点链接返回的参数,这个时候就可以改动返回值的结果,以达到想要测试的目的。
SourceTree
即是一个可视化的git工具,拥有完整的git功能,用于拉取提交托管的代码,不过业务代码过于庞大,还需要进一步熟悉使用才不易出错。
在使用上总结了一些小方法:
如果现在已经在修改代码,但是之前提交过的代码还需要二次修改,那么可以将现在修改的代码放在“已贮存”下,这样修改完之前的代码后将其恢复就不会丢失现在正在修改的代码。
还有就是合并代码的时候,软合并会保留所有现在的副本修改,强行合并会丢弃改过的工作副本。
合并步骤:
- 拉取develop最新代码
- 切到自己的分支,再右键develop,将当前变更变基到develop
- 切到develop,将自己分支合并到develop
- 有冲突的话修改冲突,兼容自己和别人的代码,一定要细心!!
oc基础及其特性
oc分类和扩展
简单的来说,在不想增添一个新类的时候,例如如果有类继承该类也需要这些被添加的方法,或是对框架提供类的扩展(没有源码,不能修改)分类只可以为被分类的类增添类方法(其实想要增添新变量也可以用@dynamic,但一般不这样做)像SDWebImage在应用中常用到其分类。
扩展像是一个匿名类可以为被扩展类增添新成员变量和方法,一般直接写在被扩展类的m文件中@implementation的上方。
在使用上需要注意分类方法不可调super,分类的方法可能与该类其他分类方法覆盖或者被覆盖并且分类方法优先级较高可能覆盖原类方法。
- 分类示例
@interface NSObject (CategoryName)//interface后是原类名,括号内是分类类名,这种情况的文件名便为“NSObject+CategoryName.h/.m”
- (void)myMethod;
@end
- 扩展示例
@interface MyClass : NSObject
@property (retain, readonly) float value;
@end
//一般的时候,Extension都是放在.m文件中@implementation的上方。
@interface MyClass () //扩展部分
@property (retain, readwrite) float value;
@end //扩展部分结束
@implementation MyClass
……
@end
Block
block可以看作将 “函数” 及 “执行其上下文” 封装起来的 “对象”,即像一个函数指针。在应用主要用于回调,因此可以实现页面反向传值等功能。其中可以在三处声明block:
- 属性用法,^ 后面就是block的名称,必须是copy!
@property (nonatomic,copy) void(^location)(NSError *error,NSDictionary*geolocation);
至于为什么必须是copy,block其实默认都是在栈区,栈区的内存是由编译器自动分配释放,保存在栈中的block,他会随着函数调用结束被销毁,导致我们在执行一个包含block的函数之后,就无法再访问这个block。因为函数结束,函数栈就销毁了,存在函数里面的block也就没有了,我们再使用block时,就会产生空指针异常。而copy将block从栈区转移到堆区,一般由程序员分配释放。
- 类外面申明,成为一种数据类型
typedef void (^CNNCloseLocationBlock)(NSDictionary *dict);
- 方法中的参数block用法,(^block xx) block名称从括号中写在外面成为参数
- (void)setGetLocationBlock:(void(^)(NSDictionary * dic))block;
但是在使用上要注意,block是异步执行,需要考虑异步产生的效果否则可能与想象中不一样。
其次就是block循环引用问题,假如self持有一个block而block内部也持有self,那就会出现循环引用,造成内存泄漏,具体解决方案如下:
@weakify(self); //在外部定义了一个weak的self_weak_变量,block对self弱引用,不会造成循环引用
[ RACObserve(self, name) subscribeNext:^(NSString*name) {
@strongify(self); //局域定义了一个strong的self指针指向self_weak
self.outputLabel.text = name; //这样self既不会被block强引用,在block结束时因为有上一行操作,self也不会被在中途释放掉
}];
(ps.@weakify@strongify要先宏定义一下,不然__weak好难写,万一是后期改的话self还要一起改weakSelf strongSelf麻烦死了)
反向传值
说到反向传值,下面整理两种反向传值的方法,其中页面一是前页面,页面二是后页面:
- 通过协议protocol来实现页面跳转后数据的反向传输
页面一:
// 在需要跳转到页面二的地方
ViewControllerTableView* vc3=[[ViewControllerTableViewalloc]init];
vc3.delegate=self;
[self.navigationControllerpushViewController:vc3 animated:YES]; // 跳转
// 实现页面二协议方法
-(void)updateValue2:(NSMutableArray*)idArray
{
NSLog(@"%@",idArray);
}
页面二:
@protocol PassValueDelegate<NSObject> // 页面二协议
- (void)updateValue2:(NSString*)str;
@end
@interfaceViewController2: UIViewController
@property(nonatomic, weak) id<PassValueDelegate>delegate;
@end
-(void)ButtonAction //需要反传数据的方法
{
if([self.delegaterespondsToSelector:@selector(updateValue2:)])
{
[self.delegateupdateValue2:cellsId]; //执行协议方法,方法定义在页面一中,因此只需传参便可实现数据反传
}
}
- 通过block来实现页面跳转后数据的反向传输
页面一:
ViewController2*vc2 = [[ViewController2alloc] init];
vc2.delegate= self;
vc2.passValueBlock= ^(NSString* _Nonnullstr) {
NSLog(@"str:%@",str);
};
页面二:
@interfaceViewController2: UIViewController
@property(nonatomic, copy) void(^passValueBlock)(NSString*str);
@end
零碎代码整理
- AFNetworking网络请求
-(void)Request {
AFHTTPSessionManager*manager = [AFHTTPSessionManagermanager];
// 要传的参数
//NSDictionary *dicPatameters = @{};
// GET请求
[manager GET:@"https://mapi.appvipshop.com/vips-mobile/rest/shop/search/brand_store/get/v3" parameters:nil progress:^(NSProgress* _NonnulldownloadProgress) { //不需要传参的话将parameters设为nil即可
} success:^(NSURLSessionDataTask* _Nonnulltask, NSDictionary* responseObject) {
NSLog(@"请求成功:%@",responseObject);
} failure:^(NSURLSessionDataTask* _Nullabletask, NSError* _Nonnullerror) {
NSLog(@"请求失败:%@",error);
} ];
}
- SDWebImage加载网络图片
SDWebImageManager*manager = [SDWebImageManagersharedManager] ; // SDWebImage 加载网络图片
[manager downloadImageWithURL:imageURL options:0progress:^(NSInteger receivedSize, NSIntegerexpectedSize) {
// progression tracking code
} completed:^(UIImage*image, NSError*error, SDImageCacheTypecacheType, BOOLfinished, NSURL*imageURL) { // 回到主线程
if(image)
{
NSLog(@"加载成功");
}
else{
NSLog(@"加载失败");
}
}];
- 判断系统明暗设置
[selfsetNeedsStatusBarAppearanceUpdate]; // 手动对下面的方法进行更新
- (UIStatusBarStyle)preferredStatusBarStyle{ //判断系统明暗模式
if(self.navigationItem.backgroundImage) {
returnUIStatusBarStyleLightContent;
} else{
if(@available(iOS13.0, *)) {
BOOLisDark = self.traitCollection.userInterfaceStyle== UIUserInterfaceStyleDark;
returnisDark ? UIStatusBarStyleLightContent: UIStatusBarStyleDarkContent;
} else{
returnUIStatusBarStyleDefault;
}
}
}
- dealloc进行内存泄漏的判断
dealloc方法会在对象的引用计数器值为0时,也就是对象即将被销毁时系统自动给对象发送一条dealloc消息,因此从dealloc方法有没有被调用,就可以判断出对象是否被销毁。但是在使用中需要注意不可直接调用dealloc 方法,并且一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃。
- (void)dealloc{
……
}