系统常用方法
1.Class
1.1 类的一些判断方法
- 是否是某个类或子类的实例
- (BOOL)isKindOfClass:(Class)aClass;
- 用来判断是否是某个类的实例
- (BOOL)isMemberOfClass:(Class)aClass;
- 用来判断是否有以某个名字命名的方法
- (BOOL)respondsToSelector:(SEL)aSelector
- 用来判断实例是否有以某个名字命名的的方法。
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
- 初始化方法
//通过纯代码创建自定义控件的时候调用此方法
- (instancetype)initWithFrame:(CGRect)frame;
// 通过storyboard 或XIB创建时调用此方法,此时控件没有初始化 为nil
- (instancetype)initWithCoder:(NSCoder *)coder
// 通过storyboard 或XIB创建时调用此方法 此时控件有值
- (void)awakeFromNib
- 第一次使用这个类的时候回调用一次
+ (void)initialize
loadView()
方法执行的时候 控制器的view = nil的时候,然后创建view 在view创建完毕时候回调viewDidLoad()
,如果你需要显示自己的view那么你需要代码创建view在loadView()
中,不用super父类的方法,因为如果super.loadView()
会默认创建一个空白的View。在viewDidLoad()
中加载,loadView首先会寻找有没有Xib文件,如果有则会根据Xib文件创建View,如果没有找到,就会创建一个空白的View
- (void)loadView
1.2 URL
1.2.1 字符串太长转URL不全问题
- 字符串太长转换成URL的时候会造成URL有省略号,显示不全,导致无法使用,我在URL里面发
initFileURLWithPath
这个初始化URL方法,于是使用这个方法,果然没有问题。 查询官方文档解释:如果确定string是一个目录,最好使用initFileURLWithPath
这个方法来转URL。 但是万一不是路径,目前不知道~~~~
[[NSURL alloc] initFileURLWithPath:@"xxx.path"]
1.3 NSinvocation
- OC中调用某个对象的方法有两种方式
- 第一种是通过performSelector: withObject:
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
但是此方法最多调用两个参数,如果有多个参数就没办法使用了 - 第二种是通过
NSInvocation
,可以实现多参数调用,示例:
- (id)performSelector:(SEL)selector withObjects:(NSArray *)values {
//使用NSinvocation来实现方法多参数调用
//1.获取调用对象中的方法签名(方法描述)
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
//2.判断这个方法存不存在
if (signature == nil) {
//如果方法不存在我们可以抛出一个异常
//@throw [NSException exceptionWithName:@"卧槽,崩溃了" reason:@"not found object selector" userInfo:nil];
//第二种方法抛出异常
[NSException raise:@"卧槽,崩溃了" format:@"%@ selector name not found",NSStringFromSelector(selector)];
}
//3.NSinvocation: 利用一个Invocation来包装一次方法调用(方法调用者,方法名,方法参数,方法返回值)
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self;
invocation.selector = selector;
//4设置方法参数 按照方法中除 self, _cmd 以外的参数个数,也就是方法参数个数
NSInteger paramsCount = signature.numberOfArguments;//获取方法参数个数(这里包含参数 self、_cmd)
//传入实际参数
paramsCount = MIN(paramsCount, values.count);
for (int i = 0; i < paramsCount; i++) {
id object = values[i];
//4.1判断里面对象是不是null
if ([object isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&object atIndex:i+2];//因为前面两个参数是 self 、_cmd
}
//4.获取返回值
id returnObject = nil;
//5.判断方法有没有返回值
if ([signature methodReturnLength]) {
[invocation getReturnValue:&returnObject];
}
//6.调用方法
[invocation invoke];
return returnObject;
}
测试代码:
- (void)test:(NSString *)name age:(NSNumber *)age sex:(NSString *)sex {
NSLog(@"name====%@ age=====%@ sex======%@",name,age,sex);
}
[self performSelector:@selector(test:age:sex:) withObjects:@[@"guoweiyong",@(4),@"男"]];
执行结果:
需要注意:
- 1.我们首先需要判断该方法存不存在,如果不存在,则直接抛出异常。
- 2.方法参数个数如果与外界传递进来参数数组元素不符,我们不能直接遍历循环设置参数,需要通过
numberOfArguments
获取参数个数,是包含self和_cmd,然后比较取最小值。还有方法的参数类型我们没办法判断类型,因为数组都是对象,所以方法中参数尽量设置为对象,如果是基本类型,那没办法实现,并且可能数据出错。如果外界传递方法参数不够,按照顺序赋值,后面不够的是为null - 调用方法之前,首先需要判断方法有没有返回值。
1.3 NSString
1.3.1字符串自带查找功能
- 从字符串的第一个字母开始查找,找到第一个位置,返货是
NSRange
对象,可以知道location
,length
- (NSRange)rangeOfString:(NSString *)searchString;
- 可以倒着查找,从字符串最后一个字符往前开始查找,找到第一个就返回,
/**
typedef NS_OPTIONS(NSUInteger, NSStringCompareOptions) {
NSCaseInsensitiveSearch = 1,
NSLiteralSearch = 2, /* Exact character-by-character equivalence */
NSBackwardsSearch = 4, /* Search from end of source string */
NSAnchoredSearch = 8, /* Search is limited to start (or end, if NSBackwardsSearch) of source string */
NSNumericSearch = 64, /* Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find */
NSDiacriticInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 128, /* If specified, ignores diacritics (o-umlaut == o) */
NSWidthInsensitiveSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 256, /* If specified, ignores width differences ('a' == UFF41) */
NSForcedOrderingSearch API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) = 512, /* If specified, comparisons are forced to return either NSOrderedAscending or NSOrderedDescending if the strings are equivalent but not strictly equal, for stability when sorting (e.g. "aaa" > "AAA" with NSCaseInsensitiveSearch specified) */
NSRegularExpressionSearch API_AVAILABLE(macos(10.7), ios(3.2), watchos(2.0), tvos(9.0)) = 1024 /* Applies to rangeOfString:..., stringByReplacingOccurrencesOfString:..., and replaceOccurrencesOfString:... methods only; the search string is treated as an ICU-compatible regular expression; if set, no other options can apply except NSCaseInsensitiveSearch and NSAnchoredSearch */
};
*/
- (NSRange)rangeOfString:(NSString *)searchString options:(NSStringCompareOptions)mask;
1.4 数组
1.4.1 数组在遍历中删除元素
- 普通的for循环
for (NSUInteger i = 0; i < mArr.count; i++) {
[mArr removeObjectAtIndex:i];
NSLog(@"mArr.count====%d",mArr.count);
}
NSLog(@"%@", mArr);
//数组长度变化: 5, 4, 3
//打印结果为:2, 4, 6,而且程序没有crash
-
由此可知普通的for循环中是可以删除元素的, 而且数组的长度是实时的变化的 ,最后结果数组中还有元素, 是因为遍历下标为0元素时,删除该元素,数组长度发生变化,遍历到下标为1时,由于数组长度发生改变,实质是相当于没有删减数组中的下标为2的元素,并且遍历次数随着,数组实际长度的改变, 遍历的次数并不是我们想象的原数组的长度次数
-
迭代器遍历
[mArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[mArr removeObject:obj];
NSLog(@"mArr.count====%d",mArr.count);
}];
//数组长度变化: 5, 4, 3
//打印结果为:2, 4, 6,而且程序没有crash
-
可知迭代器的遍历和普通的for循环遍历是一样的原理
-
for - in遍历 : 被称为快速遍历,用它来进行的循环操作将是无序的
for (NSNumber *obj in mArr) {
[mArr removeObject:obj];
}
NSLog(@"%@", mArr);
//打印结果: crash Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x2812d6a90> was mutated while being enumerated.'
- 猜测在forin快速遍历的内部有个内置的不会动态改变个数的计数器, 当你的数组做出增删后, 计数器并没有相应的增减, 这样就会导致继续通过计数器获取数组, 造成数组越界
解决办法就 :采用逆序遍历,这样删除元素就不会crah
2.控件(Control)
2.1.TextView
- 使用代理方法来实现,允许输入文字返回YES,否则NO,通过能否输入文字决定是够放弃第一响应者
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
2.2 UINavigationController
UINavigationController
控制器的Push
操作是一个入栈操作,需要注意点 是导航控制器,不支持push另外一个导航控制器- 1.
translucent
可以控制导航栏的透明度,默认值为YES,当值为YES的时候frame的定点是从导航栏从左上角的屏幕的最顶点开始算,如果设置为NO的时候,定点是从导航栏下面的左定点作为开始点
self.navigationController.navigationBar.translucent = NO;
- 2.去掉导航栏的下划线
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.navigationController.navigationBar.shadowImage = [UIImage new];
- 3.设置导航栏的背景颜色,除此之外我们还可以通过给KVC设置导航栏的背景颜色
self.navigationController.navigationBar.barTintColor = UIColor.orangeColor;
- 4.我们可以通过
titleTextAttributes
属性来设置导航栏的字体大小 - 5.可以通过
tintColor
属性来设置导航栏的按钮颜色,不管是自定义的还是 系统的都会改变 - 6.凡是在导航控制器下的scrollView都会自动设置一个上面的内边距 64/88,
self.tableView.contentInset={64,0,0,0}
,我们可以通过以下ScrollerView的这个属性不让它自动设置内边距contentInsetAdjustmentBehavior
,以前是控制器的automaticallyAdjustsScrollViewInsets
属性。
2.3 UIButton
- 自定义一个按钮,集成UIButton,如何给自定义按钮中的子控件重新布局。
//背景显示范围
- (CGRect)backgroundRectForBounds:(CGRect)bounds;
// 内容显示方位
- (CGRect)contentRectForBounds:(CGRect)bounds;
// 文本显示范围
- (CGRect)titleRectForContentRect:(CGRect)contentRect;
// 图片显示范围
- (CGRect)imageRectForContentRect:(CGRect)contentRect;
第二种方法:
- (void)layoutSubviews {
[super layoutSubviews];
//在这里设置frame
}
2.4 UIView
2.4.1单独设置View任意角度的圆角
-
- 怎么样设置View的任意一个拐角 圆角,我们可以使用
CAShapeLayer
设置layer
的mask
遮罩属性
- 怎么样设置View的任意一个拐角 圆角,我们可以使用
- (void)clipRoundWithRectCorner:(UIRectCorner)rectCorner withSize:(CGSize)radiusSize {
//创建一页遮罩CAShapeLayer
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCorner cornerRadii:radiusSize].CGPath;
self.layer.mask = shapeLayer;
}
//调用 //例子: 裁剪view的左上角 圆角为30
[self.testView clipRoundWithRectCorner:UIRectCornerTopLeft withSize:CGSizeMake(30, 30)];
执行结果:
2.4.1 给view添加一个镂空效果
- **
CAShapeLayer
**有一个填充属性,kCAFillRuleNonZero
和kCAFillRuleEvenOdd
(奇偶)规则来填充路径,从路径覆盖范围内的任意一点做一条射线(确保这条射线的长度要比路径覆盖范围要大) , 如果与该射线相交的边的数量为奇数, 则该点是路径的内部点(填充:两个路径不重叠的位置进行填充), 反之该点则是路径的外部点(不填充:两个路径重贴的不分),根据这样的原则可以形成一个镂空效果
- (void)addHollowingEffectWith:(NSArray *)pathArray {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
//设置layer的显示方式 奇偶显示规则--> 从路径覆盖范围内的任意一点做一条射线(确保这条射线的长度要比路径覆盖范围要大) , 如果与该射线相交的边的数量为奇数, 则该点是路径的内部点(填充), 反之该点则是路径的外部点(不填充)
shapeLayer.fillRule = kCAFillRuleEvenOdd;
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:self.frame];
//bezierPath.usesEvenOddFillRule
if (pathArray.count == 0) {
return;
}
for (UIBezierPath *path in pathArray) {
[bezierPath appendPath:path];
}
shapeLayer.path = bezierPath.CGPath;
self.layer.mask = shapeLayer;
}
//调用
[self.bgView addHollowingEffectWith:@[path]];
执行结果:
关于绘图的填充规则,我们可以参考这边文章,绘图的填充规则,然后在看代码,就很好理解了。
2.5 UIImage
2.5.1 UIimage的圆角优化
- IOS中添加圆角方式有两种方式,一种是基于“离屏渲染(off-screen-renderring)”,直接设置view的layer层参数即可简单实现,也很常用,但性能较低; 另外一种则是编写底层图形代码,实现“在屏渲染(on-screen-renderring)”,可以大大优化绘制性能。
/* 设置圆角半径 */
view.layer.cornerRadius = 5;
/* 将边界以外的区域遮盖住 */
view.layer.masksToBounds = YES;
这种方法最简单快速,但其实这些方法的实现是靠的“离屏渲染(off-screen-renderring)”,性能很低。
另外一种是实现 “在屏渲染(on-screen-renderring)” 用于提高性能:
+ (UIImage *)clipImageToRound:(UIImage *)originalImage {
CGSize size = originalImage.size;
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGRect rect = CGRectMake(0, 0, size.width, size.height);
//1.使用Quartz2D来进行裁剪
//获取上下文
CGContextRef contextRef = UIGraphicsGetCurrentContext();
//.增加一个圆
CGContextAddEllipseInRect(contextRef, rect);
//3裁剪
CGContextClip(contextRef);
//但是裁剪之后我们需要把 这个裁剪之后的路径绘制到图片上
[originalImage drawInRect:rect];
// //使用UIBezierPath
// UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:rect];
// //在路径上增加一个裁剪内容
// [bezierPath addClip];
// [[UIColor orangeColor] setFill];
//
// //根据添加的裁剪内容,裁剪这个rect
// UIRectClip(rect);
//
// //需要把裁剪的图片会知道上下文中
// [originalImage drawInRect:rect];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
然后直接调用这个方法,来实现图片的圆角
参考代码
2.5.2 UIImage的生成方法对比
Apple官方的文档为生成一个UIImage对象提供了两种方法:
1. imageNamed: 其参数为图片的名称;优点:加载时会缓存图片,用于图片的平凡使用;缺点:占用内存,直到应用结束
2. imageWithContentsOfFile: 其参数是图片文件的路径。有点:紧加载图片,不会缓存,用于价值次数较少的情况,降低内存消耗。
两种方式的区别:
-
imageNamed: 这个方法用一个指定的名字在系统缓存中查找并返回一个图片对象如果它存在的话。如果缓存中没有找到相应的图片,这个方法从指定的文档中加载然后缓存并返回这个对象。因此imageNamed的优点是当加载时会缓存图片。所以当图片会频繁的使用时,那么用imageNamed的方法会比较好。例如:你需要在 一个TableView里的TableViewCell里都加载同样一个图标,那么用imageNamed加载图像效率很高。系统会把那个图标Cache到内存,在TableViewCell里每次利用那个图 像的时候,只会把图片指针指向同一块内存。正是因此使用imageNamed会缓存图片,即将图片的数据放在内存中,iOS的内存非常珍贵并且在内存消耗过大时,会强制释放内存,即会遇到memory warnings。而在iOS系统里面释放图像的内存是一件比较麻烦的事情,有可能会造成内存泄漏。例如:当一 个UIView对象的animationImages是一个装有UIImage对象动态数组NSMutableArray,并进行逐帧动画。当使用imageNamed的方式加载图像到一个动态数组NSMutableArray,这将会很有可能造成内存泄露。原因很显然的。
-
imageWithContentsOfFile:仅加载图片,图像数据不会缓存。因此对于较大的图片以及使用情况较少时,那就可以用该方法,降低内存消耗。
2.6 UICollectionView
- XIB创建UICollectionView,发现在设置itemSize大小之后,不管获取xib中的FlowLayout,还是使用代理UICollectionViewDelegateFlowLayout中的代理方法来设置itemSize大小时候,发现itemSize的宽度无法改变。解决方法:
//重新创建flowLayout对象
let flowLayout = UICollectionViewFlowLayout.init()
flowLayout.itemSize = CGSize.init(width: width, height: height)
flowLayout.minimumLineSpacing = 15
flowLayout.minimumInteritemSpacing = 15
let count = cachesPenitenceTypeLists.count / 3
let rem = cachesPenitenceTypeLists.count % 3
collectionViewHeightCons.constant = CGFloat(height * CGFloat(rem == 0 ? count : (count + 1)) + 35)
//最关键一步,在此设置CollectionView的布局对象,然后itemSize的大小就可以改变了
typeCollectionView.setCollectionViewLayout(flowLayout, animated: true)
如果是XIB的话记得设置XIB的Layout选项 是coustom
3.XIB
3.1 xib的手势获取
- 1.添加手势后,以下的创建对象方发不可以通过lastObject来获取View,因为最后获取的是手势对象,可以改为firstObject
[[[NSBundle mainBundle] loadNibNamed:"name" owner:nil options:nil] lastObject]
3.2 IB_DESIGNABLE/IBInspectable 用法
- 在oc中使用 IB_DESIGNABLE/IBInspectable 来设置可视化界面的属性
@interface UIView (GYVisible)
/// view的圆角属性
@property (nonatomic, assign) IBInspectable CGFloat cornerRadio;
/// view的边框宽度
@property (nonatomic, assign) IBInspectable CGFloat borderWidth;
/// view的边框颜色
@property (nonatomic, assign) IBInspectable UIColor *borderColor;
@end
#import "UIView+GYVisible.h"
IB_DESIGNABLE
@implementation UIView (GYVisible)
- (void)setCornerRadio:(CGFloat)cornerRadio {
self.layer.cornerRadius = cornerRadio;
self.layer.masksToBounds = true;
}
- (CGFloat)cornerRadio {
return self.layer.cornerRadius;
}
- (void)setBorderWidth:(CGFloat)borderWidth {
self.layer.borderWidth = borderWidth;
}
- (CGFloat)borderWidth {
return self.layer.borderWidth;
}
- (void)setBorderColor:(UIColor *)borderColor {
self.layer.borderColor = borderColor.CGColor;
}
- (UIColor *)borderColor {
return [UIColor colorWithCGColor:self.layer.borderColor];
}
- 在swift中使用的是@IBDesignable/@IBInspectable
@IBDesignable
extension UIView {
// 不能够有初始化值
@IBInspectable var vsiable_Radius: CGFloat {
get {
return self.layer.cornerRadius;
}
set (newValue) {
self.layer.cornerRadius = newValue
self.layer.masksToBounds = true;
}
}
}
在分类中使用@property
声明属性,只是将该属性添加到该类的属性列表,但是并没有声明setter和getter方法,没有生成相应的成员变量,也没有实现setter和getter方法。需要实现它的get和set方法,这样在类的外部就能用点语法获取并设置属性,这样可以认为给类添加了属性.(一般使用Runtime来动态设置属性)
3.3 Content Hugging Priority和Content Compression Resistance Priority
Content Hugging Priority
直译成中文内容拥抱优先级,从字面意思上看就是两个视图,谁的优先级高,谁就有限环绕其内容。Content Compression Resistance Priority
该优先级直译成中文就是“内容压缩阻力优先级”。也就是视图的“内容压缩阻力优先级”越大,那么该视图中的内容越难被压缩。而该优先级小的视图,则内容优先被压缩。
当XIB布局时,但动态适应文字高度时 ,无法确定其高度时,这时候需要调试这个两个属性,来确定压缩和拉伸的优先级
参考链接:青玉伏案的博客
4.属性
4.1 readonly
- 1.在.h文件中声明只读,然后在.m文件中再次声明,这样就可以达到自己可以编辑,但是别人只能读,无法编辑
@interface ViewController : UIViewController
@property (nonatomic, readonly) NSString *name;
@end
@interface ViewController ()<UITextViewDelegate>
@property (nonatomic, readwrite) NSString *name;
@end
4.2 __nullable 和 __nonnull
- 两个新的类型修饰符:
_nullable
和_nonull
,从字面意思可知,_nullable
表示对象可以是NULL
或nil
,而_nonull
表示对象不应该为空。当我们不遵循这易规则时,编译器会给出警告。为了避免与第三方库潜在的冲突,苹果把__nonnull/__nullable
改成_Nonnull/_Nullable
。再加上苹果同样支持了没有下划线的写法nonnull/nullable
,于是就造成现在有三种写法这样混乱的局面,但是这三种写法本质上是互通的。
5.1 Other(杂项)
5.1 产生随机数
-
C语言中生成随机数的一种方法
-
1.
rand()
函数并不是一个真正的伪随机数发生器,在产生随机数前,需要系统提供的生成伪随机数序列的种子,rand根据这个种子的值产生一系列随机数。如果系统提供的种子没有变化,每次调用rand()函数生成的伪随机数序列都是一样的。 -
2.srand(unsigned seed)通过参数seed改变系统提供的种子值,从而可以使得每次调用rand()函数生成的伪随机数序列不同,从而实现真正意义上的“随机”。
-
3.通常可以利用系统时间来改变系统的种子值,即srand(time(NULL)),可以为rand()函数提供不同的种子值,进而产生不同的随机数序列
-
4.time的时间经度较低,每55ms变动一次,也就说每55ms生成一个随机种子。
-
5.由于计算机运行速度很快,执行完上述代码也不用55ms,所以如果放在for循环里面,每次得到的随机种子都是一样,而rand()是根据随机种子的值产生一系列随机数,因而每次得出的随机数都是一样的。
-
6.rand()生成的随机数范围为:( 0~RAND_MAX)。这个值很大,需要用取模的方法对随机值进行限制。
-
7.RAND_MAX的值为:0x7fffffff(这是16进制的值)换算成十进制为: 2147483647
-
iOS 有如下四种随机数方法,下面以产生 [0,100)(不包含100)的随机数为例:
srand((unsigned)time(0));
int i = rand() % 100;
srandom(time(0));
int i = random() % 100;
int i = arc4random() % 100;
int i = arc4random_uniform(100)
5.1 OC对象和CF(Core Foundation)对象桥接转换
- 使用
__bridge
关键字即可实现CF对象和OC对象之间的自由转换 - OC对象转换为CF对象
调用CF框架的方法,所有权没有改变还是属于OC对象,对象的生命周期还是由ARC自动管理,不需要手动释放
NSData *ocData = [NSData data];
CFDataRef dataRef = (__bridge CFDataRef)ocData;
- CF对象转换OC对象
调用OC对象的方法,所有权没有改变还是属于CF对象,所以需要调用CFRelease()
来完成对象的释放
CFStringRef cfString= CFURLCreateStringByAddingPercentEscapes(
NULL,
(__bridge CFStringRef)text,
NULL,
CFSTR("!*’();:@&=+$,/?%#[]"), CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding));
NSString *ocString = (__bridge_transfer CFStringRef)cfString;
- 对象的所有权转换
- CF对象转换成OC对象,需要释放CF对象的所有权,把所有权交给OC对象管理,所以需要使用桥接转换函数
CFBridgingRelease()
实现所有权的转换,也可以使用__bridge_transfer
关键字来实现这个所有权的转换过程 - OC对象转换成CF对象,CF对象需要保留该对象,所以需要桥接转换函数
CFBridgingRetain()
实现所有权的保留,防止OC对象释放使用CF对象出现悬垂指针,也可以使用关键字__bridge_retained
来实现所有权的转换过程
- CF对象转换成OC对象,需要释放CF对象的所有权,把所有权交给OC对象管理,所以需要使用桥接转换函数
NSURL *url = [[NSURL alloc] initWithString:@"http://www.baidu.com"];
CFURLRef ref = (__bridge_retained CFURLRef)url;
CFRelease(ref);
当使用_bridge_retained标识符以后,代表OC要将对象所有权交给CF对象自己来管理,所以我们要在ref使用完成以后用CFRelease将其手动释放.
此时OC即获得了对象的所有权,ARC负责自动释放该对象。不需要再使用CFRelease(cfString)
5.3 round、ceil、floor函数略解
round()
函数,如果参数是小数,则求本身的四舍五入。floor()
函数,如果参数是小数,则求取最大的整数,但是不大于本身。ceil()
函数,如果参数是小数,则求取最小整数,但是最小整数,不小于本身。
示例代码:
NSLog(@"round======%f ======%f",round(3.4),round(3.5));
NSLog(@"floor======%f ======%f",floor(3.4),floor(3.5));
NSLog(@"ceil======%f ======%f",ceil(3.4),ceil(3.5));
5.4 代码禁止自动锁屏
@property(nonatomic,getter=isIdleTimerDisabled) BOOL idleTimerDisabled; // default is NO
[UIApplication sharedApplication].idleTimerDisabled = YES; //不自动锁屏
[UIApplication sharedApplication].idleTimerDisabled = NO;//自动锁屏
5.5 控制器调用TableViewCell的block会产生循环引用
- 当前控制器中有属性UITableView,如果TableViewCell实现一个block,如果在block内部如果使用self,直接访问当前控制器中的属性,那么会产生循环引用
#import <UIKit/UIKit.h>
typedef void(^BlockTestableViewCellBlock)(void);
NS_ASSUME_NONNULL_BEGIN
@interface BlockTestableViewCell : UITableViewCell
@property (nonatomic, copy)BlockTestableViewCellBlock callback;
@end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BlockTestableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[BlockTestableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
//如果这里不使用__weak 来修饰,直接使用self了来访问时, 当pop页面时,当前控制器不会产生销毁,产生了循环引用, 其实我们通过UITableView的代理是使用weak修饰,可知,如果在TableView和Controller之间直接使用self来访问,会产生循环引用
__weak typeof(self) weakSelf = self;
cell.callback = ^{
weakSelf.tempStr = @"会不会产生循环引用吗";
NSLog(@"tempStr -======%@",weakSelf.tempStr);
};
cell.textLabel.text = [NSString stringWithFormat:@"第%ld行", (long)indexPath.row];
return cell;
}
- (void)dealloc
{
NSLog(@"ViewController=========dealloc");
}
5.6 UICollectionViewCell等分屏幕出现缝隙问题
- 出现缝隙的原因是由于Cell的个数不能不能被CollectionView的宽度整除,如何解决, 我们可以设置Collectionview的宽度为大于屏幕宽但是能够整除cell个数的最大宽度
//防止cell等分出现缝隙
let difference = Int(kScreenWidth)%7
let maxWidth = CGFloat(7 - difference) + kScreenWidth // 大于屏幕最接近屏幕能除尽7的宽
let cellWidth = maxWidth/7 // 每个cell的宽
let viewX = (kScreenWidth - maxWidth)/2 // UICollectionView 的x值
let collectionView = UICollectionView.init(frame: CGRect.init(x: viewX, y: 0, width: maxWidth, height: height), collectionViewLayout: flowLayout)
5.7 UITableView中调用reloadData()方法导致表哥偏移量错乱
1.视图漂移或者闪动原因:
因为iOS 11后系统默认开启Self-Sizing,首先要知道Self-Sizing是个什么东东。官方文档是这样解释的:大概就是说我们不用再自己去计算cell的高度了,只要设置好这两个属性,约束好布局,系统会自动计算好cell的高度。
IOS11以后,Self-Sizing默认开启,包括Headers, footers。如果项目中没使用estimatedRowHeight属性,在IOS11下会有奇奇怪怪的现象,因为IOS11之前,estimatedRowHeight默认为0,Self-Sizing自动打开后,contentSize和contentOffset都可能发生改变。
所以可以通过以下方式禁用:
在UITableview初始化的时候加入下面代码
self.tableView.estimatedRowHeight = 0;
self.tableView.estimatedSectionHeaderHeight = 0;
self.tableView.estimatedSectionFooterHeight = 0
5.8 UITextFiled判断输入中文的过程
在使用UITextField或UITextView实时实时计算content的长度的时候会出现一些偏差
可以发现UITextField或UITextView输入文字的时候输入的文字会有两个状态——选中与未选中。在实时计算长度的时候肯定计算的是选中后的text,那如何区分这个状态呢?这是就用到了markedTextRange。
如果markedTextRange
是有值的时候,说明这是在输入过程中,属于未选中的状态, 如果该属性的值为nil,表示已经选中了
5.9 label文字垂直居中
如果你指向UILable的文字在垂直方向居中, 你可以设置属性baselineAdjustment
的值为UIBaselineAdjustmentAlignCenters
,这样Lable的文字就回在垂直方向居中
6. Xcode配置问题
6.1 CONFIGURATION_BUILD_DIR
CONFIGURATION_BUILD_DIR
: 设置SDK的编译产生SDK的路径,没有设置解释默认路径, 如果设置了编译生成的SDK就回在对应的设置路径下
6.2 在工程中导入framework工程源码
- 首先把framework的工程源码复制到项目中的某个文件夹内
- 在项目工程中 使用
Add Files To xxxx
把xxxxx.xcodeproj
文件导入进来,这样就可以了,可以直接在项目工程中修改framework的内容然后测试。 - 注意:framework中如果有依赖framework的话,记得咋项目工程中也需要依赖相应的framework,因为framework编译出来是不会包含其中依赖的framework内容的