chenzhao

  • java
  • iOS
  • IT
知识积累
不积跬步无以至千里
  1. 首页
  2. iOS
  3. 正文

IOS知识点-小技巧-小笔记(2)

2017年 3月 1日 80点热度 0人点赞 0条评论

xcode中,全局去掉项目warning的开头在Builder Setttings->Inhibit All Warnings(抵制所有的警告).当把它设置为Yes时,编译项目就不会出现warning警告了.
因为部分引入的第三方的项目 去掉警告


清理icon 角标 对于ios11 没有去测试

/**
 刷新本地和服务器的图标 (不在appIcon上显示推送数量,但是在系统通知栏保留推送通知的方法)

 @param noti <#noti description#>
 */
- (void)refreshPushBageValue:(NSNotification *)noti{
    NSNumber *value = [noti object];
   
   
    //
    if(kiOS11Later){
        /*
         iOS 11后,直接设置badgeNumber = -1就生效了
         */
        
        [UIApplication sharedApplication].applicationIconBadgeNumber = value.integerValue;
        [JPUSHService setBadge:value.integerValue];
    }else{
        // 原理是 发送了一个本地推送 无消息的, 如果本地推送有处理 需要处理下
        UILocalNotification *clearEpisodeNotification = [[UILocalNotification alloc] init];
        clearEpisodeNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:(0.3)];
        clearEpisodeNotification.timeZone = [NSTimeZone defaultTimeZone];
        /// 根据这个消息 不处理本地
        clearEpisodeNotification.alertTitle = @"清理icon";
        clearEpisodeNotification.applicationIconBadgeNumber = value.integerValue;
        
       
        [[UIApplication sharedApplication] scheduleLocalNotification:clearEpisodeNotification];
    }
}

终端代理(临时方案)

在终端中执行以下代码, 1 为http 代理, 2 为全部代理 . ;电脑开启代理软件
export http_proxy=http://127.0.0.1:1087  当前临时方案, 关闭后就不走代理了
export all_proxy=socks5://127.0.0.1:1086  这个是临时 都走代理 不光http ,会很快

通过宏定义判断是否引入的是framework,反之则使用双引号,实用!

#if __has_include(<xxx/xxx.h>)
#import <xxx/xxx.h>
#else
#import "xxx.h"
#endif

#if __has_include(<GPUImage/GPUImageFramework.h>)
#import <GPUImage/GPUImageFramework.h>
#else
#import "GPUImage.h"

swift 打印内存地址

Unmanaged.passRetained(self as AnyObject)//这个引用计数会+1
Unmanaged.passUnretained(self as AnyObject) // 这个引用计数+0

如下输出 Unmanaged(_value: <boosjdance.LiveShootViewController: 0x10bead530>)


部分服务端更改, 验证了cookie app 中没有cookie 清除 造成服务端返回400 和413, 然后加入cookie 清理


URLCache.shared.removeAllCachedResponses()
        let cookies =  HTTPCookieStorage.shared.cookies
        if cookies != nil && cookies!.count>0{
            for cookie in cookies!{
                HTTPCookieStorage.shared.deleteCookie(cookie)
            }
        }

//iOS9.0以上使用的方法
        if #available(iOS 9.0, *) {
            let dataStore = WKWebsiteDataStore.default()
            dataStore.fetchDataRecords(ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(), completionHandler: { (records) in
                for record in records{
                    //清除本站的cookie
                    if record.displayName.contains("sina.com"){//这个判断注释掉的话是清理所有的cookie
                        WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {
                            //清除成功
                            print("清除成功\(record)")
                        })
                    }
                }
            })
        } else {
            //ios8.0以上使用的方法
            let libraryPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.libraryDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
            let cookiesPath = libraryPath! + "/Cookies"
            try!FileManager.default.removeItem(atPath: cookiesPath)
        }



1 宏
1.1 __has_include
用于判断是否包含某些头文件 例如:
#if __has_include(<UIKit/UIKit.h>)

1.2 NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END
两者搭配使用,在这两个宏之间的所有函数变量都是不可空。如果在这两个宏之间,想要可空的函数变量,需要单独设置 nullable关键字。

1.3 UNAVAILABLE_ATTRIBUTE
不可用。在方法或者属性 加上这个宏之后。将变成不可用。

1.4 UI_APPEARANCE_SELECTOR
加到属性后面,所有该属性的实例都统一设置。


IPv6 问题:
1 使用域名
2 手动转换
假设访问http://67.218.154.33
转换为ipv6 形式 : http://[::ffff:67.218.154.33]

注 找了一个解释:https://www.jianshu.com/p/1312e98cd35b

---- 获取类名---
oc

/// switf 代码会带程序名
NSString *selfClassName = NSStringFromClass([self class]);
/// 去除程序名
selfClassName = [selfClassName componentsSeparatedByString:@"."].lastObject;
   

swift

// 返回内部类名

print("class: \(object_getClassName(self))")

// 返回应用程序名+类名

print("class: \(NSStringFromClass(self.dynamicType))")

// 返回应用程序名+类名,并去掉应用程序名

print("class: \(NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!)")

// 返回应用程序名+类名+内存地址

print("class: \(self)")

// 返回应用程序名+类名+内存地址

print("class: \(self.description)")

// 返回类名

print("class: \(self.dynamicType)")


Swizzle touchesBegan:withEvent:事故

得实现override touches 方法原因见
地址


ios 设备目录获取

// 获取沙盒主目录路径
NSString *homeDir = NSHomeDirectory();
// 获取Documents目录路径
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
// 获取Library的目录路径
NSString *libDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
// 获取Caches目录路径
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 获取tmp目录路径
NSString *tmpDir =  NSTemporaryDirectory();

程序目录

NSLog(@"%@",[[NSBundle mainBundle] bundlePath]);
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"apple" ofType:@"png"];
UIImage *appleImage = [[UIImage alloc] initWithContentsOfFile:imagePath];


libstdc++适配Xcode10与iOS12

原因是苹果在XCode10和iOS12中移除了libstdc++这个库,由libc++这个库取而代之,苹果的解释是libstdc++已经标记为废弃有5年了,建议大家使用经过了llvm优化过并且全面支持C++11的libc++库。

beta 版本的xcode 10 拷贝下xcode9 的文件(注 模拟器 还是崩溃)

cp /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/libstdc++.* /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/lib/

cp /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/libstdc++.* /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/lib/

根本解决办法:

如果你自己的业务模块使用了libstdc++,那么就把模块代码重新调整为依赖libc++,然后重新检查是否存在问题,重新编译
如果你引用的三方库使用了libstdc++,那么向三方库寻求支持,进行升级


查看应用初始化时间
在Xcode中,可以通过设置环境变量来查看App的启动时间,DYLD_PRINT_STATISTICS和DYLD_PRINT_STATISTICS_DETAILS。(这个是更详细的)
如图设置:控制台会输出时间

一个App在执行main函数前包括app delegate的系列方法如applicationWillFinishLaunching时,会做许多系统级别的准备.而在iOS10之前,开发者很难清楚自己App为何启动加载慢.而通过在工程的scheme中添加环境变量DYLD_PRINT_STATISTICS,设置Value为1,App启动加载时就会有启动过程的日志输出. 现在(iOS 10之后)Apple对DYLD_PRINT_STATISTICS的日志输出结果进行了简化,使得更容易让开发者理解.


iOS开发之使用P3图片导致崩溃的解决方法

最近app刚上架,突然收到大面积投诉....一看bugly,9.0-9.3的机器无一幸免,由于项目里有些图标是我直接从阿里图库下载的,问了UI P3,16进制的图片是什么他也说不清,索性让他重新做图了,这个问题只要图片是UI做图基本就可避免

1.打包成ipa

2.把ipa的后缀改成zip,解压缩(这时候会看到一个Payload文件夹)

3.打开终端  输入  cd 

4.把 Payload  拖动到终端里(这里的拖动只是为了获取这个文件在电脑上的地址),  回车

5.在终端输入  find . -name 'Assets.car'  回车(会输出找到的位置)

6.在终端输入  sudo xcrun --sdk iphoneos assetutil --info ./Assets.car > /tmp/Assets.json  回车
(car 地址可以根据上面找到的位置填入)

7.在终端输入  open /tmp/Assets.json  回车

8.这时候会打开一个text 搜索 DisplayGamut  看看后面是不是P3 如果搜索到的是p3  图片格式还是不对,如果是空或者搜索到显示的不是P3,那图片就对了,根据Name去查找项目里的这张图片吧,然后将其替换.

转自点击


UIView 设置单边圆角

 /// 设置单边圆角
    private func setMaskLayer(){
        let corner = self.height/2
        let maskPath =  UIBezierPath(roundedRect: self.bounds, byRoundingCorners: [UIRectCorner.bottomLeft,UIRectCorner.topLeft], cornerRadii: CGSize.init(width: corner, height: corner));
        let maskLayer = CAShapeLayer.init()
        maskLayer.frame = self.bounds
        maskLayer.path = maskPath.cgPath
        
        self.layer.mask = maskLayer;
        
    }

jsonp 转json 正则

str.match(".?({.}).*")返回数组第一个


记录一个逗号分隔用法


    // 多值的设置,使用逗号分隔
    // 注意:if let语句中不能使用&& || 条件
    // if let中只要有任何一个条件为nil,就跳出循环
    if let name = oName, age = oAge {
        print("Hi~" + name + "年龄:" + String(age))
    }

IOS脚本打包 IPA(.APP转.IPA)

将要转化的.app文件放到 convertToIpa.sh 同目录之中

运行 convertToIpa.sh 脚本

打开 Terminal,cd 到 convertToIpa.sh 的目录,执行

./convertToIpa.sh appName(.app 的名字)

如果提示 permission denied,则用 chmod 777 distribute.sh 命令赋予权限后,再执行一次。

等脚本之行结束后,会在当前文件夹下生成 appName 文件夹,里面的 appName.ipa 就是我们最终想要的包。


#!/bin/bash

mkdir 1

mkdir1/Payload

cp -r 1.app1/Payload/1.app

cp Icon.png1/iTunesArtwork

cd 1

zip -r1.ipa Payload iTunesArtwork

exit 0

xcode 编译线程数

1.获取当前内核数:
$ sysctl -n hw.ncpu
2.设置编译线程数:
$ defaults write com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks 8
3.获取编译线程数:
$ defaults read com.apple.dt.Xcode IDEBuildOperationMaxNumberOfConcurrentCompileTasks
4.显示编译时长:
$ defaults write com.apple.dt.Xcode ShowBuildOperationDuration YES

iOS12.1 使用 UINavigationController + UITabBarController( UITabBar 磨砂),设置hidesBottomBarWhenPushed后,在 pop 后,会引起TabBar布局异常

// .h
@interface CYLTabBar : UITabBar
@end

// .m
#import "CYLTabBar.h"

/**
*  用 block 重写某个 class 的指定方法
*  @param targetClass 要重写的 class
*  @param targetSelector 要重写的 class 里的实例方法,注意如果该方法不存在于 targetClass 里,则什么都不做
*  @param implementationBlock 该 block 必须返回一个 block,返回的 block 将被当成 targetSelector 的新实现,所以要在内部自己处理对 super 的调用,以及对当前调用方法的 self 的 class 的保护判断(因为如果 targetClass 的 targetSelector 是继承自父类的,targetClass 内部并没有重写这个方法,则我们这个函数最终重写的其实是父类的 targetSelector,所以会产生预期之外的 class 的影响,例如 targetClass 传进来  UIButton.class,则最终可能会影响到 UIView.class),implementationBlock 的参数里第一个为你要修改的 class,也即等同于 targetClass,第二个参数为你要修改的 selector,也即等同于 targetSelector,第三个参数是 targetSelector 原本的实现,由于 IMP 可以直接当成 C 函数调用,所以可利用它来实现“调用 super”的效果,但由于 targetSelector 的参数个数、参数类型、返回值类型,都会影响 IMP 的调用写法,所以这个调用只能由业务自己写。
*/
CG_INLINE BOOL
OverrideImplementation(Class targetClass, SEL targetSelector, id (^implementationBlock)(Class originClass, SEL originCMD, IMP originIMP)) {
   Method originMethod = class_getInstanceMethod(targetClass, targetSelector);
   if (!originMethod) {
       return NO;
   }
   IMP originIMP = method_getImplementation(originMethod);
   method_setImplementation(originMethod, imp_implementationWithBlock(implementationBlock(targetClass, targetSelector, originIMP)));
   return YES;
}
@implementation CYLTabBar

+ (void)load {
   /* 这个问题是 iOS 12.1 Beta 2 的问题,只要 UITabBar 是磨砂的,并且 push viewController 时 hidesBottomBarWhenPushed = YES 则手势返回的时候就会触发。
    
    出现这个现象的直接原因是 tabBar 内的按钮 UITabBarButton 被设置了错误的 frame,frame.size 变为 (0, 0) 导致的。如果12.1正式版Apple修复了这个bug可以移除调这段代码(来源于QMUIKit的处理方式)*/
   static dispatch_once_t onceToken;
   dispatch_once(&onceToken, ^{
       if (@available(iOS 12.1, *)) {
           OverrideImplementation(NSClassFromString(@"UITabBarButton"), @selector(setFrame:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP originIMP) {
               return ^(UIView *selfObject, CGRect firstArgv) {
                   
                   if ([selfObject isKindOfClass:originClass]) {
                       // 如果发现即将要设置一个 size 为空的 frame,则屏蔽掉本次设置
                       if (!CGRectIsEmpty(selfObject.frame) && CGRectIsEmpty(firstArgv)) {
                           return;
                       }
                   }
                   
                   // call super
                   void (*originSelectorIMP)(id, SEL, CGRect);
                   originSelectorIMP = (void (*)(id, SEL, CGRect))originIMP;
                   originSelectorIMP(selfObject, originCMD, firstArgv);
               };
           });
       }
   });
}
@end

来源 https://github.com/ChenYilong/iOS12AdaptationTips/issues/3


pod 'AFNetworking'                 //不显式指定依赖库版本,表示每次都获取最新版本
pod 'AFNetworking', '~>0'          //高于0的版本,写这个限制和什么都不写是一个效果,都表示使用最新版本

pod 'AFNetworking', '~> 0.1.2'     //使用大于等于0.1.2但小于0.2的版本
pod 'AFNetworking', '~>0.1'        //使用大于等于0.1但小于1.0的版本

pod 'AFNetworking', '2.0'          //只使用2.0版本
pod 'AFNetworking', '= 2.0'        //只使用2.0版本

pod 'AFNetworking', '> 2.0'        //使用高于2.0的版本
pod 'AFNetworking', '>= 2.0'       //使用大于或等于2.0的版本
pod 'AFNetworking', '< 2.0'        //使用小于2.0的版本
pod 'AFNetworking', '<= 2.0'       //使用小于或等于2.0的版本

pod 'AFNetworking', :git => 'http://gitlab.xxxx.com/AFNetworking.git', :branch => 'R20161010'  //指定分支 

pod 'AFNetworking',  :path => '../AFNetworking'  //指定本地库


//    //排序
//    NSSortDescriptor *isDefault = [NSSortDescriptor sortDescriptorWithKey:@"isDefault" ascending:NO];
//    NSSortDescriptor *isUse = [NSSortDescriptor sortDescriptorWithKey:@"isOnlyUse" ascending:NO];
//    [self.dataArray sortUsingDescriptors:@[isDefault,isUse]];


系统装 carthage 造成的终端编译ipa找不到编辑器

sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer/

------node link--- 失败问题

Could not symlink share/doc/node/gdbinit
Target /usr/local/share/doc/node/gdbinit
already exists. You may want to remove it:
rm '/usr/local/share/doc/node/gdbinit'

解决
1 sudo chown -R $USER /usr/local
2 brew link --overwrite node


xcode 打包多taget 版本号自动同步
添加 Shell 脚本; 在Xcode Build Phases -> 添加 Run Script;
注: 会影响打包, 直接自动打包shell 脚步直接处理, 方法同样


# Type a script or drag a script file from your workspace to insert its path.
if [ CONFIGURATION == Release ]; then
echo "Bumping build number..."
plist={INFOPLIST_FILE}

buildnum=(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "{plist}")
if [[ "{buildnum}" == "" ]]; then
echo "No build number inplist"
exit 2

fi
echo "Bumped build number to buildnum"
buildnum=(expr buildnum + 1)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersionbuildnum" "{INFOPLIST_FILE}"

echo "Update build number to Current Project Version"
agvtool new-version -allbuildnum

echo "Keep Extension Target build version and number as same as app"
buildver=(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "{plist}")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString buildver" "SRCROOT/ZBiOSNotificationService/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionStringbuildver" "SRCROOT/ZBiOSNotificationService/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion buildnum" "SRCROOT/ZBiOSNotificationService/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersionbuildnum" "SRCROOT/ZBiOSNotificationService/Info.plist"

else
echo $CONFIGURATION "build - Not bumping build number."
fi



时间的一个处理

   // 获取代表公历的NSCalendar对象
            let gregorian = Calendar(
                identifier: .gregorian)
            // 获取当前日期
            let dt = Date()
            // 定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
//            let unitFlags: NSCalendar.Unit = [.year, .month, .day, .hour, .minute, .second, .weekday]
            // 获取不同时间字段的信息
            let comp = gregorian.dateComponents(
                [.year, .month, .day, .hour, .minute, .second, .weekday],
                from: dt)
            // 获取各时间字段的数值
           
            // 再次创建一个NSDateComponents对象
            var comp2 = DateComponents()
            // 设置各时间字段的数值
            comp2.year = comp.year
            comp2.month = comp.month
            comp2.day = comp.day
            comp2.hour = comp.hour
            comp2.minute = 34
            // 通过NSDateComponents所包含的时间字段的数值来恢复NSDate对象
            let date = gregorian.date(from: comp2)
            if let date = date {
                print("获取的日期为:\(date)")
            }
// 获取代表公历的NSCalendar对象
  NSCalendar *gregorian = [[NSCalendar alloc]
   initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
  // 获取当前日期 
  NSDate* dt = [NSDate date];
  // 定义一个时间字段的旗标,指定将会获取指定年、月、日、时、分、秒的信息
  unsigned unitFlags = NSCalendarUnitYear | 
   NSCalendarUnitMonth |  NSCalendarUnitDay |
   NSCalendarUnitHour |  NSCalendarUnitMinute |
   NSCalendarUnitSecond | NSCalendarUnitWeekday;
  // 获取不同时间字段的信息
  NSDateComponents* comp = [gregorian components: unitFlags 
   fromDate:dt];
  // 获取各时间字段的数值
  NSLog(@"现在是%ld年" , comp.year);
  NSLog(@"现在是%ld月 " , comp.month);
  NSLog(@"现在是%ld日" , comp.day);
  NSLog(@"现在是%ld时" , comp.hour);
  NSLog(@"现在是%ld分" , comp.minute);
  NSLog(@"现在是%ld秒" , comp.second);
  NSLog(@"现在是星期%ld" , comp.weekday);
  // 再次创建一个NSDateComponents对象
  NSDateComponents* comp2 = [[NSDateComponents alloc] 
   init];
  // 设置各时间字段的数值
  comp2.year = 2013;
  comp2.month = 4;
  comp2.day = 5;
  comp2.hour = 18;
  comp2.minute = 34;
  // 通过NSDateComponents所包含的时间字段的数值来恢复NSDate对象
  NSDate *date = [gregorian dateFromComponents:comp2];
  NSLog(@"获取的日期为:%@" , date);


pod 组件,使用时, 头文件报 重复导入,使用检查头文件的方式导入

#if __has_include(<ShareSDK/ShareSDK.h>)
#import <ShareSDK/ShareSDK.h>
#else

#endif

格式化数据

NSLog(@"%02ld",2);
NSLog(@"%0.2f",0.2656);
NSLog(@"%0.2f",0.2646);

注意的是%0.2f 是会对数字进行一个四舍五入
14:57:28.506 App[4010:98217] 02
2016-06-20 14:57:28.507 App[4010:98217] 0.27
2016-06-20 14:57:28.507 App[4010:98217] 0.26

标签: 暂无
最后更新:2022年 11月 11日

陈昭

IT 程序员

打赏 点赞
< 上一篇
下一篇 >

文章评论

取消回复

COPYRIGHT © 2022 chenzhao. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang