iOS开发-NSNotification源码原理学习

文章目录
问题
1. 实现原理
对于addObserver方法,为什么需要object参数?
都传入null对象会怎么样
addObserver源码逻辑
2.通知的发送时同步的,还是异步的
3. NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
4.NSNotificationQueue和runloop的关系
5.如何保证通知接收的线程在主线程
6.多次添加同一个通知会是什么结果?多次移除通知呢?
7.下面的方式能接收到通知吗?为什么
问题
苹果并没有开源相关代码,但是可以读下 GNUStep 的源码

或者这里下载
链接:https://pan.baidu.com/s/1F25GgeLxqKjeo10Zgfr2OQ
密码:qpka

从下列问题触发,探索下NSNotification的实现原理

实现原理(结构设计、通知如何存储的、name&observer&SEL之间的关系等)
通知的发送时同步的,还是异步的
NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
NSNotificationQueue是异步还是同步发送?在哪个线程响应
NSNotificationQueue和runloop的关系
如何保证通知接收的线程在主线程
页面销毁时不移除通知会崩溃吗
多次添加同一个通知会是什么结果?多次移除通知呢
下面的方式能接收到通知吗?为什么

1 // 发送通知
2 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@”TestNotification” object:@1];
3 // 接收通知
4 [NSNotificationCenter.defaultCenter postNotificationName:@”TestNotification” object:nil];

1. 实现原理
对于addObserver:selector:name:object:会创建一个observation

1 typedef
2   id
3   SEL
4   struct Obs
5   int
6   struct NCTbl
7 } Observation;

对于Observation持有observer

在iOS SDK 8之前:不是一个类似OC中的weak类型,持有的相当与一个__unsafe_unretain指针对象,当对象释放时,会访问已经释放的对象,造成BAD_ACCESS。
在iOS SDK 8之后:持有的是weak类型指针,对nil对象performSelector不再会崩溃
name和Observation是映射关系,observer和sel包含在Observation结构体中。

此外,NSNotification维护了GSIMapTable表的结构,用于存储Observation,分别是nameless,name,cache,nameless存储没有传入名字的通知,named存储传入了名字的通知,cache用于快速缓存.

1 #define CHUNKSIZE 128
2 #define CACHESIZE 16
3 typedef struct NCTbl {
4   Observation *wildcard; /* Get ALL messages. */
5   GSIMapTable nameless; /* Get messages for any name. */
6   GSIMapTable named; /* Getting named messages only. */
7   unsigned lockCount; /* Count recursive operations. */
8   NSRecursiveLock *_lock; /* Lock out other threads. */
9   Observation *freeList;
10   Observation **chunks;
11   unsigned numChunks;
12   GSIMapTable cache[CACHESIZE];
13   unsigned short chunkIndex;
14   unsigned short cacheIndex;
15 } NCTable;

这里值得注意nameless和named的结构,虽然同为hash表

1
2 在nameless表中:
3 GSIMapTable的结构如下
4 object : Observation
5 object : Observation
6 object : Observation
7
8 —————————-
9 在named表中:
10 GSIMapTable结构如下:
11 name : maptable
12 name : maptable
13 name : maptable
14
15 maptable的结构如下
16 object : Observation
17 object : Observation
18 object : Observation

关于GSIMap结构

对于addObserver方法,为什么需要object参数?
– (void)addObserver:(id)observer selector:(SEL)aSelector name:(nullable NSNotificationName)aName object:(nullable id)anObject;

就是addObserver其实不用传入name也可以,传入object,当postNotification方法同样发出这个object时,就会触发通知方法。

例如这样的写法:

1 – (void)viewDidLoad {
2     [super viewDidLoad];
3     // Do any additional setup after loading the view.
4     NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
5     MyObject *obj = [MyObject new];
6     [center addObserver:self selector:@selector(doAction:) name:nil object:obj];
7     dispatch_async(dispatch_get_global_queue(0, 0), ^{
8         [center postNotificationName:@”TEST” object:obj];
9     });
10 }
11
12 – (void)doAction:(NSNotification*)sender {
13     NSLog(@”%s %@ %@”,__FUNCTION__,sender,[NSThread currentThread]);
14 }

参考 https://www.jianshu.com/p/83770200d476

都传入null对象会怎么样
你可能也注意到了,addObserver方法name和object都可以为空,这表示将会把observer赋值为 wildcard,他将会监听所有的通知。

addObserver源码逻辑
addObserver的逻辑如下

1. 根据传入的selector和observer创建Observation,并存入maptable中,如果已存在,则是从cache中取。
2. 如果name存在,则向named的maptable表中插入元素,key为name,value为GSIMapTable,GSIMapTable中存key为object,value为Observation,转入5。如果不存在则进入3
3. 如果object存在,则向nameless的maptable表中插入元素,key为object,value为Observation。如果不存在,则进入4,否则转入5
4. name和object都为空,则Observation->next=wildcard,将老的wildcard赋值为next指针,然Observation对象置为wildcard,wildcard = Observation
5.结束

2.通知的发送时同步的,还是异步的
postNotificationName的底层实现是

– (void) _postAndRelease: (NSNotification*)notification
由于内部会读取TABLE

lockNCTable(TABLE);
… //找到对应的observer对象
unlockNCTable(TABLE);

…//执行performSelector方法

1 lockNCTable(TABLE);
2 … //找到对应的observer对象
3 unlockNCTable(TABLE);
4
5 …//执行performSelector方法
6
7 lockNCTable(TABLE);
8 GSIArrayEmpty(a); //释放临时创建的数组对象 – 用于存储observer的
9 unlockNCTable(TABLE);

同步异步这个问题,由于TABLE资源的问题,同一个线程会按顺序执行,自然是同步的。

3. NSNotificationCenter接受消息和发送消息是在一个线程里吗?如何异步发送消息
由于是使用的performSelector方法,没有进行转线程,默认是postNotification方法的线程。

1
2 [o->observer performSelector: o->selector
3                                 withObject: notification];
4

对于异步发送消息,可以使用NSNotificationQueue,queue顾明意思,我们是需要将NSNotification放入queue中执行的。

有三种状态

1 typedef NS_ENUM(NSUInteger, NSPostingStyle) {
2     NSPostWhenIdle = 1,      // 当runloop处于空闲状态时post
3     NSPostASAP = 2,    // 当当前runloop完成之后立即post
4     NSPostNow = 3    // 立即post
5 };

 

1 NSNotification *noti = [NSNotification notificationWithName:@”111″ object:nil];
2 [[NSNotificationQueue defaultQueue] enqueueNotification:noti postingStyle:NSPostASAP];

参考文章 https://www.jianshu.com/p/356f7af4f2ee

4.NSNotificationQueue和runloop的关系
NSNotificationQueue的执行是依赖于runloop的,它的三种模式各自的执行时机不一样。

1 typedef NS_ENUM(NSUInteger, NSPostingStyle) {
2     NSPostWhenIdle = 1,      // 当runloop处于空闲状态时post
3     NSPostASAP = 2,    // 当当前runloop完成之后立即post
4     NSPostNow = 3    // 立即post
5 };

例如

1 void asyncQueueNotiInRunloop() {
2     dispatch_async(dispatch_get_global_queue(0, 0), ^{
3         NSLog(@”1″);
4         NSLog(@”%@”, [NSThread currentThread]);
5
6         //NSPostWhenIdle
7         //NSPostASAP
8         //NSPostNow
9         NSNotification *notification = [NSNotification notificationWithName:@”TEST” object:nil];
10         [[NSNotificationQueue defaultQueue] enqueueNotification:notification postingStyle:NSPostWhenIdle coalesceMask:NSNotificationNoCoalescing forModes:@[NSDefaultRunLoopMode]];
11         // run runloop
12         [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSRunLoopCommonModes];
13         CFRunLoopRun();
14         NSLog(@”3″);
15     });
16 }

如果去掉run runloop部分的代码,则无法触发通知

5.如何保证通知接收的线程在主线程
由于通知的发出使用performSeletor实现,如果需要保证接收的线程在主线程,可以:

保证主线程发出
接收到通知后跳转到主线程,苹果建议使用NSMachPort进行消息转发到主线程。
https://blog.csdn.net/shengpeng3344/article/details/90206265

使用block接口addObserverForName:object:queue:usingBlock:

1 – (id <NSObject>)addObserverForName:(nullable NSNotificationName)name object:(nullable id)obj queue:(nullable NSOperationQueue *)queue usingBlock:(void (^)(NSNotification *note))block API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));
2     // The return value is retained by the system, and should be held onto by the caller in
3     // order to remove the observer with removeObserver: later, to stop observation.

页面销毁时不移除通知会崩溃吗?
对于Observation持有observer

在iOS SDK 8之前:不是一个类似OC中的weak类型,持有的相当与一个__unsafe_unretain指针对象,当对象释放时,会访问已经释放的对象,造成BAD_ACCESS。
在iOS SDK 8之后:持有的是weak类型指针,对nil对象performSelector不再会崩溃
所以说不一定会崩溃,但是根据代码严谨是需要remove的

6.多次添加同一个通知会是什么结果?多次移除通知呢?
由于源码中并不会进行重复过滤,所以添加同一个通知,等于就是添加了2次,回调也会触发两次。

为什么会触发两次呢,因为- (void) postNotificationName: (NSString*)name object: (id)object的逻辑是这样的:

1. 查找所有是wildcard类型的Observation,加入数组array,即既不监听name也不监听object
2. 查找指定object但未指定name的Observation,加入数组array
3. 查找指定了相同name和object的Observation,加入数组array,当name一致但object不一致时,也不会加入到数组array,但如果传入的object!=nil,则会将name对应的maptable中,所有key为nil的Observation也加入数组。
4. 遍历array,执行performSelector
5. 清空array

第三步的意思是:如果发出一个通知,方法中传入了对象object,那么那些只监听通知name,object设置为nil的当然也可以收到,object匹配了的也可以收到,object不匹配的则收不到。

关于多次移除,并没有问题,因为会去map中查找,找到才会删除。当name和object都为nil时,会移除所有关于该observer的WILDCARD

7.下面的方式能接收到通知吗?为什么

1
2 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:@”TestNotification” object:@1];
3
4 [NSNotificationCenter.defaultCenter postNotificationName:@”TestNotification” object:nil];

根据postNotification的实现,会找到key为TestNotification的maptable,再从中选择key为nil的observation,所以是找不到以@1为key的observation的

iOS开发-ViewController的生命周期相关

文章目录
ViewController生命周期
加载流程
didReceiveMemoryWarning
View的layoutSubviews
Runloop相关
view的drawRect:方法
ViewController生命周期
加载流程

%title插图%num

1 1.init或者initWithCoder:(NSCoder *)aDecoder:(如果使用storyboard或者xib)
2 2.loadView:加载view
3 3.viewDidLoad:view加载完毕
4 4.viewWillAppear:控制器的view将要显示
5 5.viewWillLayoutSubviews:控制器的view将要布局子控件
6 6.viewDidLayoutSubviews:控制器的view布局子控件完成
7 这期间系统可能会多次调用viewWillLayoutSubviews、viewDidLayoutSubviews俩个方法
8
9 7.viewDidAppear:控制器的view完全显示
10 8.viewWillDisappear:控制器的view即将消失的时候
11 这期间系统也会调用viewWillLayoutSubviews 、viewDidLayoutSubviews 两个方法
12
13 9.viewDidDisappear:控制器的view完全消失的时候

didReceiveMemoryWarning

Discussion Your app never calls this method directly. Instead, this
method is called when the system determines that the amount of
available memory is low.

You can override this method to release any additional memory used by
your view controller. If you do, your implementation of this method
must call the super implementation at some point.

当app收到内存警告的时候会发消息给视图控制器。
app从来不会直接调用这个方法,而是当系统确定可用内存不足的时候采取调用。
如果你想覆写这个方法来释放一些控制器使用的额外内存,你应该在你的实现方法中调用父类的实现方法。

View的layoutSubviews
init初始化不会触发layoutSubviews。
addSubview会触发layoutSubviews。
改变一个UIView的frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化。
滚动一个UIScrollView引发UIView的重新布局会触发layoutSubviews。
旋转Screen会触发父UIView上的layoutSubviews事件。
直接调用setNeedsLayout 或者 layoutIfNeeded。

Runloop相关
在非主页面加载时

1 * thread #1, queue = ‘com.apple.main-thread’, stop reason = breakpoint 1.1
2   * frame #0: 0x0000000100529264 GSWatermarkView`-[GSWaterMarkView layoutSubviews](self=0x000000011d801410, _cmd=”layoutSubviews”) at GSWaterMarkView.m:110
3     frame #1: 0x00000001a77db5b0 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2156
4     frame #2: 0x00000001a2fe7af0 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 68
5     frame #3: 0x00000001a9d81c0c QuartzCore`-[CALayer layoutSublayers] + 292
6     frame #4: 0x00000001a9d81f14 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 484
7     frame #5: 0x00000001a9d953fc QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 140
8     frame #6: 0x00000001a9cda184 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double) + 296
9     frame #7: 0x00000001a9d05228 QuartzCore`CA::Transaction::commit() + 684
10     frame #8: 0x00000001a7362d6c UIKitCore`_afterCACommitHandler + 144
11     frame #9: 0x00000001a324bf5c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 36
12     frame #10: 0x00000001a3246bfc CoreFoundation`__CFRunLoopDoObservers + 420
13     frame #11: 0x00000001a32471ac CoreFoundation`__CFRunLoopRun + 1292
14     frame #12: 0x00000001a3246978 CoreFoundation`CFRunLoopRunSpecific + 480
15     frame #13: 0x00000001ad376534 GraphicsServices`GSEventRunModal + 108
16     frame #14: 0x00000001a7338f0c UIKitCore`UIApplicationMain + 1940
17     frame #15: 0x000000010052814c GSWatermarkView`main(argc=1, argv=0x000000016f8df940) at main.m:14
18     frame #16: 0x00000001a30c6f04 libdyld.dylib`start + 4

在初始界面加载时

1 * thread #1, queue = ‘com.apple.main-thread’, stop reason = breakpoint 1.1
2   * frame #0: 0x0000000100b05264 GSWatermarkView`-[GSWaterMarkView layoutSubviews](self=0x000000010130f210, _cmd=”layoutSubviews”) at GSWaterMarkView.m:110
3     frame #1: 0x00000001a77db5b0 UIKitCore`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2156
4     frame #2: 0x00000001a2fe7af0 libobjc.A.dylib`-[NSObject performSelector:withObject:] + 68
5     frame #3: 0x00000001a9d81c0c QuartzCore`-[CALayer layoutSublayers] + 292
6     frame #4: 0x00000001a9d81f14 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 484
7     frame #5: 0x00000001a9d953fc QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 140
8     frame #6: 0x00000001a9cda184 QuartzCore`CA::Context::commit_transaction(CA::Transaction*, double) + 296
9     frame #7: 0x00000001a9d05228 QuartzCore`CA::Transaction::commit() + 684
10     frame #8: 0x00000001a7350d20 UIKitCore`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 84
11     frame #9: 0x00000001a324c95c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 28
12     frame #10: 0x00000001a324c0e0 CoreFoundation`__CFRunLoopDoBlocks + 268
13     frame #11: 0x00000001a32470e0 CoreFoundation`__CFRunLoopRun + 1088
14     frame #12: 0x00000001a3246978 CoreFoundation`CFRunLoopRunSpecific + 480
15     frame #13: 0x00000001ad376534 GraphicsServices`GSEventRunModal + 108
16     frame #14: 0x00000001a7338f0c UIKitCore`UIApplicationMain + 1940
17     frame #15: 0x0000000100b0414c GSWatermarkView`main(argc=1, argv=0x000000016f303940) at main.m:14
18     frame #16: 0x00000001a30c6f04 libdyld.dylib`start + 4

可以看出在app启动时,初始界面View的layoutSubviews由__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__触发,后续的界面由__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__触发

view的drawRect:方法
直接调用setNeedsDisplay,或者setNeedsDisplayInRect:触发drawRect:,但是有个前提条件是rect大小不能为0。
drawRect的调用时机是在viewWillAppear和viewDidAppear之间。且在View的layoutSubviews之后

1 2020-03-03 14:05:58.384185+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController loadView]]
2 2020-03-03 14:05:58.384252+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidLoad]]
3 2020-03-03 14:05:58.385593+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView didMoveToSuperview]]
4 2020-03-03 14:05:58.385686+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewWillAppear:]]
5 2020-03-03 14:05:58.387915+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewWillLayoutSubviews]]
6 2020-03-03 14:05:58.387956+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidLayoutSubviews]]
7 2020-03-03 14:05:58.387975+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView layoutSubviews]]
8 2020-03-03 14:05:58.388046+0800 GSWatermarkView[1318:1216023]  GSWaterMarkView [-[GSWaterMarkView drawRect:]]
9 2020-03-03 14:05:58.427785+0800 GSWatermarkView[1318:1216023]  ViewController [-[ViewController viewDidAppear:]]

调用sizeToFit,会触发drawRect的调用。
通过设置contentMode属性值为UIViewContentModeRedraw。那么将在每次设置或更改frame的时候自动调用drawRect:。
app启动时,添加到初始界面的堆栈,还是CALayer触发

iOS开发-逆向注入SDK之MonkeyDev注入打包

文章目录
MonkeyDev
使用
注入SDK
MonkeyDev
强大的工具集,MonkeyDev ,使用它行了!

安装教程见 Wiki

使用
拿着之前通过 frida砸壳 的 ipa包,先创建一个 MonkeyDev 工程。这里以 qqmusic 为例

%title插图%num
将砸壳的 ipa 放到 qqmusic/TargetApp/ 下,然后拖拽至工程文件中。

设置主工程的证书,dylib的不用设置

%title插图%num

提示 Showing All Messages Signing for “qqmusicDylib” requires a development team. Select a development team in the Signing & Capabilities editor. ,此时选择 qqmusicDylib->Build Settings->Add User-Defined Setting

%title插图%num

添加 CODE_SIGNING_ALLOWED 为 NO,再运行即可。

file not found: /usr/lib/libstdc++.dylib 问题

原因是新版本xcode去掉了libstdc++这个库,从老版本复制过来即可,这里直接使用别人的 https://github.com/devdawei/libstdc-

注入SDK
可以运行之后,我们在工程中初始化pod,

1 修改pod文件,注释use_frameworks!
2 # platform :ios, ‘9.0’
3
4 target ‘qqmusic’ do
5   # Comment the next line if you don’t want to use dynamic frameworks
6   # use_frameworks!
7   pod ‘GrowingAnalytics-cdp/Autotracker’
8   # Pods for qqmusic
9
10 end
11
12 target ‘qqmusicDylib’ do
13   # Comment the next line if you don’t want to use dynamic frameworks
14   # use_frameworks!
15   pod ‘GrowingAnalytics-cdp/Autotracker’
16   # Pods for qqmusicDylib
17
18 end

查找对应的appdelegate类,使用class-dump命令
class-dump -H xxx.app -o yourDir/Headers
1
发现其 AppDelegate 类叫 XXXXAppDelegate

然后使用logos注入SDK初始化代码,使用文档查看官网 http://iphonedevwiki.net/index.php/Logos

%title插图%num

1 // See http://iphonedevwiki.net/index.php/Logos
2
3 #import <UIKit/UIKit.h>
4 #import “GrowingAutotracker.h”
5
6 static NSString *const kGrowingProjectId = @”91eaf9b283361032″;
7
8 %hook XXXXAppDelegate
9
10 – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
11         BOOL result = %orig;
12     GrowingTrackConfiguration *configuration = [GrowingTrackConfiguration configurationWithProjectId:kGrowingProjectId];
13     configuration.debugEnabled = YES;
14     configuration.impressionScale = 1.0;
15     configuration.dataCollectionServerHost = @”https://run.mocky.io/v3/08999138-a180-431d-a136-051f3c6bd306″;
16     [GrowingAutotracker startWithConfiguration:configuration launchOptions:launchOptions];
17         return result;
18 }
19
20 %end

然后再编译运行,至此,已经可以在App中调试SDK,并有相关日志输出了。

mac开发-10.15检测屏幕录制权限

在Mac os 10.15之后,屏幕录制权限需要获取才能正确录屏,否则只能录制桌面背景以及自身app的影像。即可以截屏,但截不到其他app的内容。
文章目录
屏幕录制权限检测
屏幕录制授权申请
CGWindowListCreateImage
隐私页面跳转
清除某个App的权限记录
清除某个隐私权限的全部内容
遇到的问题
屏幕录制权限检测
对于Mac os 10.15的屏幕录制权限检测,使用如下方法为*佳:

1 – (BOOL)canRecordScreen
2 {
3     if (@available(macOS 10.15, *)) {
4         CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
5         NSUInteger numberOfWindows = CFArrayGetCount(windowList);
6         NSUInteger numberOfWindowsWithName = 0;
7         for (int idx = 0; idx < numberOfWindows; idx++) {
8             NSDictionary *windowInfo = (NSDictionary *)CFArrayGetValueAtIndex(windowList, idx);
9             NSString *windowName = windowInfo[(id)kCGWindowName];
10             if (windowName) {
11                 numberOfWindowsWithName++;
12             } else {
13                 //no kCGWindowName detected -> not enabled
14                 break; //breaking early, numberOfWindowsWithName not increased
15             }
16
17         }
18         CFRelease(windowList);
19         return numberOfWindows == numberOfWindowsWithName;
20     }
21     return YES;
22 }

另外一种方法是下面这种?,在权限没有设置的时候没问题,但是在权限设置后,会导致mac os 10.15下的系统崩溃,直接回到登陆界面。所以没有使用这个

1 – (BOOL)canRecord{
2     CGDisplayStreamRef stream = CGDisplayStreamCreate(CGMainDisplayID() , 1, 1, kCVPixelFormatType_32ABGR, nil, ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef  _Nullable frameSurface, CGDisplayStreamUpdateRef  _Nullable updateRef) {
3
4     });
5
6     BOOL canRecord = stream != NULL;
7     if (stream) {
8         CFRelease(stream);
9     }
10
11     NSLog(@”canRecord : %ld”,canRecord);
12     return canRecord;
13 }

屏幕录制授权申请
通过截屏都可以向系统获取权限

1 – (void)showScreenRecordingPrompt{
2
3   /* macos 10.14 and lower do not require screen recording permission to get window titles */
4   if(@available(macos 10.15, *)) {
5     /*
6      To minimize the intrusion just make a 1px image of the upper left corner
7      This way there is no real possibilty to access any private data
8      */
9     CGImageRef c = CGWindowListCreateImage(
10                                                     CGRectMake(0, 0, 1, 1),
11                                                     kCGWindowListOptionOnScreenOnly,
12                                                     kCGNullWindowID,
13                                                     kCGWindowImageDefault);
14
15       CFRelease(screenshot);
16
17 }

截取主屏幕全部的内容

1 CGRect mainRect = CGDisplayBounds(CGMainDisplayID());
2 CGImageRef desktopImage = CGWindowListCreateImage(mainRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageBestResolution | kCGWindowImageShouldBeOpaque);
3 NSImage* image = [[NSImage alloc]initWithCGImage:desktopImage size:_screenAsBtn.frame.size];
4 CGImageRelease(desktopImage);

截取某个子程序的图片

1 [_windowList removeAllObjects];
2 CFArrayRef windowListArray = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
3 NSArray *windows = CFBridgingRelease(CGWindowListCreateDescriptionFromArray(windowListArray));
4 for(NSMutableDictionary* dic in windows){
5     NSString* layerStr = [dic objectForKey:(__bridge id)kCGWindowLayer];
6     CGRect bounds;
7     CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[dic objectForKey:@”kCGWindowBounds”], &bounds);
8     NSRectFromCGRect(bounds);
9     if ([layerStr intValue] == 0 &&(bounds.size.width>10 && bounds.size.height>10)) {
10         [_windowList addObject:dic];
11     }
12 }
13 CFRelease(windowListArray);
14 //下面是for循环中内容,取每张图片
15 NSMutableDictionary* entry = [_windowList objectAtIndex:i];
16 int ownerPID = [[entry objectForKey:(__bridge id)kCGWindowNumber] intValue];
17 NSString* name =[entry objectForKey:(__bridge id)kCGWindowName];
18
19 NSString* owerName = [entry objectForKey:(__bridge id)kCGWindowOwnerName];
20 CGImageRef imageRef = CGWindowListCreateImage(CGRectNull,  kCGWindowListOptionIncludingWindow, ownerPID, kCGWindowImageShouldBeOpaque);
21 NSImage* image = [[NSImage alloc]initWithCGImage:imageRef size:NSZeroSize];
22 CGImageRelease(imageRef);

隐私页面跳转

1 – (void)openSetting{
2     NSString *urlString = @”x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture”;
3     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
4 }

清除某个App的权限记录
使用tccutil reset All com.xxx.xxx来清除,com.xxx.xxx为bundle id

清除某个隐私权限的全部内容
重置摄像头访问: tccutil reset Camera
重置麦克风访问: tccutil reset Microphone
重置屏幕录制:tccutil reset ScreenCapture

遇到的问题
遇到权限一直要获取,获取后提示关闭该程序,进入程序后,又需要获取的死循环。
这里我重启mac就好了,不知道是10.15系统原因否。

2019云计算高光时刻:乱云飞渡 传统IT大溃败

前言:2019年,物理机*后一张王牌也败给了云计算,无论从成本还是性能的角度,都没有不选云计算的理由,这是一个时代的终结。

2019的云计算市场格局,依旧是马太效应凸显、大者恒大的趋势继续,但在这个过程中,也存在不少的变数和高光时刻。

%title插图%num

这一年,Oracle败走中国,IBM深陷裁员风波,EMC卖身戴尔后持续低迷,云计算不仅保持着强劲的发展势头,还大举杀入传统IT厂商的腹地,搅得乱云飞渡。

这一年,人工智能、物联网、5G、微服务等持续与云计算的深度融合,将“含云量=含金量”的烙印深深刻在企业数字化转型的发动机上。

这一年,云计算成为企业*热门的话题,但凡大会言必称全面上云,你不知道云出门都不好意思跟人打招呼,多少企业在做年终总结的时候都在掂量自己有多少“含云量”。

2019年的云计算注定不平凡,因为它并不满足按部就班的迭代更新,而是不断寻找突破点,塑造了了转折点,充满了闪光点。2019年云计算有哪些高光时刻,一起来回顾一下:

全面上云拐点已到:再不上云就晚了

云时代的热度也已经持续了10年以上,但云对传统IT的渗透速度却始终不够快,这一情况从2019年开始将被改写。阿里云智能总裁张建锋在上海云峰会上指出,今年是从传统IT向云计算全面转移的分水岭,与之相印证的,是企业纷纷将IT系统全面上云,以及各种政务云纷纷上线。

以浙江政府为例,已有135个部门、1000多个系统在政务云上运行,未来3000多个系统将全部迁到阿里云上运行。掌上办公平台覆盖了浙江省市县网格六级政府工作人员,目前已建的组织数是25万个,上线应用是684个,有超过118万公务人员使用。除了浙江,广东、江苏、河南、重庆等省市加快了上云的步伐,各地政府纷纷成立云计算+大数据局,推动“云+数”的一体化建设。

%title插图%num

企业上云更是激烈,从2018年开始,服装行业先掀起了一股上云潮,安踏、李宁、特步、百丽、雅戈尔等陆续上云,看上的不仅是云的稳定和弹性,还有背后独特的数据中台和业务中台。以特步为例,其历时3年与阿里云合作打造了“特步全渠道零售平台”,给企业带来净利润61%的飙升。

银泰、大润发、联华等大型商超也加入上云阵营,摆脱了过去一到大促就出问题的窘境。银泰在2019年双11期间的峰值支撑能力达到了平日的20倍之多,这对一个有限空间内的消费形态来说是个非常傲人的成绩。

业内人士见面的谈话不是“吃了么?”,而是“上云了么?”。企业们纷纷开始用“含云量”向外传递数字化升级的信号。全面上云的“拐点”,也许正是云计算通向第二个十年的新“起点”。

传统IT大溃败 云计算杀入大银行核心系统

谁会杀死传统IT?这个答案可能要从金融行业身上找。

金融行业被视为“*冠上的明珠”,长久以来,银行、证券等产业都是高端IT镇守的重地,但云计算还是把这扇重兵把守的城门撬开了个大口。

中国金融监管部门,对于以云计算为代表的创新技术,一直秉持开放态度。早在2016年,银监会就提出要求:“2020年,银行业面向互联网场景的重要信息系统,必须全部迁移至云计算架构平台,其他系统迁移比例不低于60%。”

%title插图%num

目前,我国金融行业IT架构和云架构的格局,是由IBM为代表的传统IT服务商,阿里云为代表的互联网机构,兴业数金、山东城商联盟等银行系金融云子公司三方主导。

虽然IBM们的市场份额仍是*,但早已不是是当年那般高高在上。这一年,腾讯云为张家港农商银行开发了分布式核心系统,让商业银行用上了国产数据库;百度云与浦发银行联合开发的基于区块链技术的金融级联盟链治理平台项目顺利落地;阿里云更是与国内某金融机构合作启动去IBM大机的工作,并已制定出分阶段的技术方案:*步是先把IBM大机外围的系统逐渐迁移到金融级分布式的云平台之上,然后第二步,开始把大机上的核心应用往这个云平台上迁移。

阿里云新金融事业部总裁刘伟光日前接受媒体采访时透露,阿里云已经准备好进入大型银行核心系统,目标是推翻IBM大机的垄断地位。

目前,国内还有20多台IBM大机,全部在金融领域,分布在中国*顶级的大型银行,以及全国性的金融交易中枢系统,一台大机一年维护成本高达上亿元,这也是IBM赖以生存、利润率*高的产品之一。

当传统IT的核心地带开始崩塌,就离全面溃败不远了。

机器学习之加载查看数据集

机器学习之加载查看数据集

sklearn datasets模块
一、导入数据集
sklearn.datasets模块主要提供了一些导入、在线下载及本地生成数据集的方法。
主要有三种形式:datasets.load_()、datasets.fetch_()及datasets.make_*()的方法。*为数据集名称
① datasets.load_dataset_name():sklearn包自带的小数据集
②datasets.fetch_dataset_name():比较大的数据集,主要用于测试解决实际问题,支持在线下载
③datasets.make_dataset_name():构造数据集
两种方法导入数据集

from sklearn.datasets import load_digits
digits=load_digits()
print(digits)

from sklearn import datasets
data=datasets.load_digits()
print(data)

二、查看数据集

print(data.data) #查看样本数据
print(data.data[0])
print(data.data.shape) #(1797,64)
print(data.images) #查看图像格式(二维)的样本数据
print(data.images[0])
print(data.images.shape) #与data样本数据格式不同 (1797,8,8)
print(data.target) #标签数组[0 1 2…..8 9 8]
print(data.target.shape) #每个样本都有对应的标签值
print(data.target_names) #[0 1 2 3 4 5 6 7 8 9] 标签名称

三、可以通过使用python的数据可视化库matplotlib查看图片

import matplotlib.pyplot as plt
plt.imshow(data.images[0]) #0序列*张图
plt.imshow(data.images[0],cmap=matplotlib.cm.binary) #转为灰度图

四、figure对图像进行处理再显示

from sklearn import datasets
# 加载 `digits` 数据集
digits = datasets.load_digits()
# 导入 matplotlib
import matplotlib.pyplot as plt
# 设置图形大小(宽、高)以英寸为单位
fig = plt.figure(figsize=(6, 6))
# 设置子图形布局,如间隔之类…
fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05)
# 对于64幅图像中的每一幅
for i in range(64):
# 初始化子图:在8×8的网格中,在第i+1个位置添加一个子图
ax = fig.add_subplot(8, 8, i + 1, xticks=[], yticks=[])
# 在第i个位置显示图像
ax.imshow(digits.images[i], cmap=plt.cm.binary, interpolation=’nearest’)
# 用目标值标记图像
ax.text(0, 7, str(digits.target[i]))

# 显示图形
plt.show()

example:
显示digits.images的前7个手写数字图像,并用对应的目标值标记图像

from sklearn import datasets
# 加载 `digits` 数据集
digits = datasets.load_digits()
# 导入 matplotlib
import matplotlib.pyplot as plt
# 把图像和目标标签组合成一个列表
images_and_labels = list(zip(digits.images, digits.target))
# 对于列表(前8项)中的每个元素
for index, (image, label) in enumerate(images_and_labels[:8]):
# 在第i+1个位置初始化一个2X4的子图
plt.subplot(2, 4, index + 1)
# 不要画坐标轴
plt.axis(‘off’)
# 在所有子图中显示图像
plt.imshow(image, cmap=plt.cm.gray_r,interpolation=’nearest’)
# 为每个子图添加一个标题(目标标签)
plt.title(‘Training: ‘ + str(label))
# 显示图形
plt.show()

 

SOLO代码阅读解析

SOLO代码阅读解析

SOLO是一种直接预测instance mask的范式,摒弃了之前top-down和bottom-up两种主流的实例分割方法,从而pipeline更加简洁直观。这篇文章以官方代码中的demo为例,简单梳理一下SOLO在inference时的流程。整个代码基于mmdetection。

首先是demo.inference_demo.py

config_file = ‘../configs/solo/decoupled_solo_r50_fpn_8gpu_3x.py’
# download the checkpoint from model zoo and put it in `checkpoints/`
checkpoint_file = ‘../checkpoints/DECOUPLED_SOLO_R50_3x.pth’

# build the model from a config file and a checkpoint file
model = init_detector(config_file, checkpoint_file, device=’cuda:0′)

# test a single image
img = ‘demo.jpg’
result = inference_detector(model, img)

show_result_ins(img, result, model.CLASSES, score_thr=0.25, out_file=”demo_out.jpg”)

上述代码很简单,init_detector创建model,inference_detector做正向inference,并且show出*后的result。核心在于init_detector和inference_detector。这两个function存在于mmdet.apis中,下面看下这个模块:

mmdet.apis.inferece.py

def init_detector(config, checkpoint=None, device=’cuda:0′):
“””Initialize a detector from config file.

Args:
config (str or :obj:`mmcv.Config`): Config file path or the config
object.
checkpoint (str, optional): Checkpoint path. If left as None, the model
will not load any weights.

Returns:
nn.Module: The constructed detector.
“””
if isinstance(config, str):
config = mmcv.Config.fromfile(config)
elif not isinstance(config, mmcv.Config):
raise TypeError(‘config must be a filename or Config object, ‘
‘but got {}’.format(type(config)))
config.model.pretrained = None
model = build_detector(config.model, test_cfg=config.test_cfg)
if checkpoint is not None:
checkpoint = load_checkpoint(model, checkpoint)
if ‘CLASSES’ in checkpoint[‘meta’]:
model.CLASSES = checkpoint[‘meta’][‘CLASSES’]
else:
warnings.warn(‘Class names are not saved in the checkpoint\’s ‘
‘meta data, use COCO classes by default.’)
model.CLASSES = get_classes(‘coco’)
model.cfg = config # save the config in the model for convenience
model.to(device)
model.eval()
return model

def inference_detector(model, img):
“””Inference image(s) with the detector.

Args:
model (nn.Module): The loaded detector.
imgs (str/ndarray or list[str/ndarray]): Either image files or loaded
images.

Returns:
If imgs is a str, a generator will be returned, otherwise return the
detection results directly.
“””
cfg = model.cfg
device = next(model.parameters()).device # model device
# build the data pipeline
test_pipeline = [LoadImage()] + cfg.data.test.pipeline[1:]
test_pipeline = Compose(test_pipeline)
# prepare data
data = dict(img=img)
data = test_pipeline(data)
data = scatter(collate([data], samples_per_gpu=1), [device])[0]
# forward the model
with torch.no_grad():
result = model(return_loss=False, rescale=True, **data)
return result

对于init_detector,其核心函数是build_detector,根据config文件信息创建模型,并将checkpoint加载进来;而inference_detector更简单了,首先做一系列augmentation,然后调用model做inference即可。

那么接下来仍然是两个分支,build_detector是如何创建模型的,以及该模型如何做inference,分开来说。

build_detector
build_detector方法存在于mmdet.model.builder.py:

from mmdet.utils import build_from_cfg
from .registry import (BACKBONES, DETECTORS, HEADS, LOSSES, NECKS,
ROI_EXTRACTORS, SHARED_HEADS)

def build(cfg, registry, default_args=None):
if isinstance(cfg, list):
modules = [
build_from_cfg(cfg_, registry, default_args) for cfg_ in cfg
]
return nn.Sequential(*modules)
else:
return build_from_cfg(cfg, registry, default_args)

def build_backbone(cfg):
return build(cfg, BACKBONES)

def build_neck(cfg):
return build(cfg, NECKS)

def build_roi_extractor(cfg):
return build(cfg, ROI_EXTRACTORS)

def build_shared_head(cfg):
return build(cfg, SHARED_HEADS)

def build_head(cfg):
return build(cfg, HEADS)

def build_loss(cfg):
return build(cfg, LOSSES)

def build_detector(cfg, train_cfg=None, test_cfg=None):
return build(cfg, DETECTORS, dict(train_cfg=train_cfg, test_cfg=test_cfg))

build_detector方法又调用了build方法,而build方法中调用了build_from_cfg。注意:在调用build方法中传入了DETECTORS这个注册器(Registry,一个类,传入的参数该class的一个实例,每一个部分i.e. backbone,FPN etc. 都对应一个Registry实例),可以先理解为创建这些module以及分开进行管理。

接着看mmdet.utils.registry.py中的build_from_cfg:

def build_from_cfg(cfg, registry, default_args=None):
“””Build a module from config dict.

Args:
cfg (dict): Config dict. It should at least contain the key “type”.
registry (:obj:`Registry`): The registry to search the type from.
default_args (dict, optional): Default initialization arguments.

Returns:
obj: The constructed object.
“””
assert isinstance(cfg, dict) and ‘type’ in cfg
assert isinstance(default_args, dict) or default_args is None
args = cfg.copy()
obj_type = args.pop(‘type’)
if mmcv.is_str(obj_type):
obj_cls = registry.get(obj_type)
if obj_cls is None:
raise KeyError(‘{} is not in the {} registry’.format(
obj_type, registry.name))
elif inspect.isclass(obj_type):
obj_cls = obj_type
else:
raise TypeError(‘type must be a str or valid type, but got {}’.format(
type(obj_type)))
if default_args is not None:
for name, value in default_args.items():
args.setdefault(name, value)
return obj_cls(**args)

这里其实就是对注册器进行注册的部分,也就是说通过config中的字典来对模型进行搭建。obj_cls就是要创建的module,如SOLO,ResNet,FPN等等,只有某个注册器中有配置文件中存在的type时,才会对该注册器进行register,通过args中的dict得到相应的module。这里一开始obj_cls返回的是SOLO(可以refer下配置文件),所以我们要找到SOLO这个模型的文件:

mmdet.models.detectors.solo.py

@DETECTORS.register_module
class SOLO(SingleStageInsDetector):

def __init__(self,
backbone,
neck,
bbox_head,
train_cfg=None,
test_cfg=None,
pretrained=None):
super(SOLO, self).__init__(backbone, neck, bbox_head, None, train_cfg,
test_cfg, pretrained)

可见*行用了一个装饰器,也就是说在创建SOLO实例的时候,首先就自动调用装饰器中的方法,并且把SOLO这个类作为参数,注册到注册器DETECTORS里面。而SOLO又是继承自SingleStageInsDetector,所以接下来重点是SingleStageInsDetector类:

mmdet.models.detectors.single_stage_ins.py

@DETECTORS.register_module
class SingleStageInsDetector(BaseDetector):

def __init__(self,
backbone,
neck=None,
bbox_head=None,
mask_feat_head=None,
train_cfg=None,
test_cfg=None,
pretrained=None):
super(SingleStageInsDetector, self).__init__()
self.backbone = builder.build_backbone(backbone)
if neck is not None:
self.neck = builder.build_neck(neck)
if mask_feat_head is not None:
self.mask_feat_head = builder.build_head(mask_feat_head)

self.bbox_head = builder.build_head(bbox_head)
self.train_cfg = train_cfg
self.test_cfg = test_cfg
self.init_weights(pretrained=pretrained)

上面是SingleStageInsDetector的核心代码,之前是将args作为参数传入作为这里的初始化。根据之前的config,依次创建模型的backbone,neck,bbox_head以及test_config(这里是inference),这些部分的创建又对应到builder中的函数,每一个module对应一个Registry,然后根据相应的config文件中的参数建立不同的module,*后都作为类内部变量,集中在这一个SingleStageInsDetector中。具体每一个module创建的代码就不贴了,无非是将args传递进去,根据现有的代码创建相应的模块。

至此模型的创建工作大致如此,下面来看Inference的过程。

Inference
SOLO类的forward继承自BaseDetector,其forward方法如下:

def forward_test(self, imgs, img_metas, **kwargs):
。。。。。。

if num_augs == 1:
return self.simple_test(imgs[0], img_metas[0], **kwargs)
else:
return self.aug_test(imgs, img_metas, **kwargs)

@auto_fp16(apply_to=(‘img’, ))
def forward(self, img, img_meta, return_loss=True, **kwargs):
if return_loss:
return self.forward_train(img, img_meta, **kwargs)
else:
return self.forward_test(img, img_meta, **kwargs)

以单gpu为例,调用的是simple_test,这个函数在SingleStageInsDetector中被重写过,如下:

def extract_feat(self, img):
x = self.backbone(img)
if self.with_neck:
x = self.neck(x)
return x

def simple_test(self, img, img_meta, rescale=False):
x = self.extract_feat(img)
outs = self.bbox_head(x, eval=True)

if self.with_mask_feat_head:
mask_feat_pred = self.mask_feat_head(
x[self.mask_feat_head.
start_level:self.mask_feat_head.end_level + 1])
seg_inputs = outs + (mask_feat_pred, img_meta, self.test_cfg, rescale)
else:
seg_inputs = outs + (img_meta, self.test_cfg, rescale)
seg_result = self.bbox_head.get_seg(*seg_inputs)
return seg_result

这里Inference的顺序依次是backbone->neck->bbox_head,backbone为ResNet50,neck为FPN,bbox_head为(decoupled)solo_head。所以前面特征提取部分的代码很简单,就不做过多赘述。主要来看下bbox_head:

mmdet.models.anchor_heads.decoupled_solo_head.py

@HEADS.register_module
class DecoupledSOLOHead(nn.Module):
def __init__(self,
num_classes,
in_channels,
seg_feat_channels=256,
stacked_convs=4,
strides=(4, 8, 16, 32, 64),
base_edge_list=(16, 32, 64, 128, 256),
scale_ranges=((8, 32), (16, 64), (32, 128), (64, 256), (128, 512)),
sigma=0.4,
num_grids=None,
cate_down_pos=0,
with_deform=False,
loss_ins=None,
loss_cate=None,
conv_cfg=None,
norm_cfg=None):
super(DecoupledSOLOHead, self).__init__()
self.num_classes = num_classes
self.seg_num_grids = num_grids
self.cate_out_channels = self.num_classes – 1
self.in_channels = in_channels
self.seg_feat_channels = seg_feat_channels
self.stacked_convs = stacked_convs
self.strides = strides
self.sigma = sigma
self.cate_down_pos = cate_down_pos
self.base_edge_list = base_edge_list
self.scale_ranges = scale_ranges
self.with_deform = with_deform
self.loss_cate = build_loss(loss_cate)
self.ins_loss_weight = loss_ins[‘loss_weight’]
self.conv_cfg = conv_cfg
self.norm_cfg = norm_cfg
self._init_layers()

def _init_layers(self):
norm_cfg = dict(type=’GN’, num_groups=32, requires_grad=True)
self.ins_convs_x = nn.ModuleList()
self.ins_convs_y = nn.ModuleList()
self.cate_convs = nn.ModuleList()

for i in range(self.stacked_convs):
#*层+1表示采用coordconv concat上的position(如果非decouple则+2)
chn = self.in_channels + 1 if i == 0 else self.seg_feat_channels
# ins_x分支几个卷积+norm模块
self.ins_convs_x.append(
ConvModule(
chn,
self.seg_feat_channels,
3,
stride=1,
padding=1,
norm_cfg=norm_cfg,
bias=norm_cfg is None))
# ins_y分支几个卷积+norm模块
self.ins_convs_y.append(
ConvModule(
chn,
self.seg_feat_channels,
3,
stride=1,
padding=1,
norm_cfg=norm_cfg,
bias=norm_cfg is None))

chn = self.in_channels if i == 0 else self.seg_feat_channels
# cate分支几个卷积+norm模块
self.cate_convs.append(
ConvModule(
chn,
self.seg_feat_channels,
3,
stride=1,
padding=1,
norm_cfg=norm_cfg,
bias=norm_cfg is None))

self.dsolo_ins_list_x = nn.ModuleList()
self.dsolo_ins_list_y = nn.ModuleList()
#每一个level对应的num_grid不同,针对所有level的feature设计对应维度的卷积
for seg_num_grid in self.seg_num_grids:
self.dsolo_ins_list_x.append(
nn.Conv2d(
self.seg_feat_channels, seg_num_grid, 3, padding=1))
self.dsolo_ins_list_y.append(
nn.Conv2d(
self.seg_feat_channels, seg_num_grid, 3, padding=1))
self.dsolo_cate = nn.Conv2d(
self.seg_feat_channels, self.cate_out_channels, 3, padding=1)

def forward(self, feats, eval=False):
# for i in feats:
# print(i.shape)
# torch.Size([1, 256, 200, 304])
# torch.Size([1, 256, 100, 152])
# torch.Size([1, 256, 50, 76])
# torch.Size([1, 256, 25, 38])
# torch.Size([1, 256, 13, 19])

new_feats = self.split_feats(feats)
# for i in new_feats:
# print(i[0].shape)
# torch.Size([256, 100, 152])
# torch.Size([256, 100, 152])
# torch.Size([256, 50, 76])
# torch.Size([256, 25, 38])
# torch.Size([256, 25, 38])

featmap_sizes = [featmap.size()[-2:] for featmap in new_feats]
upsampled_size = (featmap_sizes[0][0] * 2, featmap_sizes[0][1] * 2)
# print(upsampled_size) (200, 304)
ins_pred_x, ins_pred_y, cate_pred = multi_apply(self.forward_single, new_feats,
list(range(len(self.seg_num_grids))),
eval=eval, upsampled_size=upsampled_size)
return ins_pred_x, ins_pred_y, cate_pred

def split_feats(self, feats):
return (F.interpolate(feats[0], scale_factor=0.5, mode=’bilinear’),
feats[1],
feats[2],
feats[3],
F.interpolate(feats[4], size=feats[3].shape[-2:], mode=’bilinear’))

def forward_single(self, x, idx, eval=False, upsampled_size=None):
ins_feat = x
cate_feat = x
# ins branch
# concat coord
x_range = torch.linspace(-1, 1, ins_feat.shape[-1], device=ins_feat.device)
y_range = torch.linspace(-1, 1, ins_feat.shape[-2], device=ins_feat.device)
y, x = torch.meshgrid(y_range, x_range)
y = y.expand([ins_feat.shape[0], 1, -1, -1])
x = x.expand([ins_feat.shape[0], 1, -1, -1])
# print(ins_feat.shape)
# print(x.shape)
ins_feat_x = torch.cat([ins_feat, x], 1)
ins_feat_y = torch.cat([ins_feat, y], 1)
# print(ins_feat_x.shape) (1, 256 + 1, ?, ?)

for ins_layer_x, ins_layer_y in zip(self.ins_convs_x, self.ins_convs_y):
ins_feat_x = ins_layer_x(ins_feat_x)
ins_feat_y = ins_layer_y(ins_feat_y)

ins_feat_x = F.interpolate(ins_feat_x, scale_factor=2, mode=’bilinear’)
ins_feat_y = F.interpolate(ins_feat_y, scale_factor=2, mode=’bilinear’)

ins_pred_x = self.dsolo_ins_list_x[idx](ins_feat_x)
ins_pred_y = self.dsolo_ins_list_y[idx](ins_feat_y)
# print(ins_pred_x.shape) 对应到每个feat_map对应的grid (1,256,?,?)->(1,40/36/24/16/12,?,?)

# cate branch
for i, cate_layer in enumerate(self.cate_convs):
if i == self.cate_down_pos:
seg_num_grid = self.seg_num_grids[idx] # idx对应特征图的level,不同level的num_grid不同
cate_feat = F.interpolate(cate_feat, size=seg_num_grid, mode=’bilinear’)
cate_feat = cate_layer(cate_feat)

cate_pred = self.dsolo_cate(cate_feat)
# print(cate_pred.shape) (1, 80, num_grid, num_grid)

if eval:
ins_pred_x = F.interpolate(ins_pred_x.sigmoid(), size=upsampled_size, mode=’bilinear’)
ins_pred_y = F.interpolate(ins_pred_y.sigmoid(), size=upsampled_size, mode=’bilinear’)
cate_pred = points_nms(cate_pred.sigmoid(), kernel=2).permute(0, 2, 3, 1)
return ins_pred_x, ins_pred_y, cate_pred

上面的代码是solo_head正向传播以后得到的结果:ins_pred_x, ins_pred_y, cate_pred。但并不是完整的Inference,*终的maks生成还需要进行下面两个函数的操作:

def get_seg(self, seg_preds_x, seg_preds_y, cate_preds, img_metas, cfg, rescale=None):
assert len(seg_preds_x) == len(cate_preds)
num_levels = len(cate_preds)
# print(num_levels) 5
featmap_size = seg_preds_x[0].size()[-2:]
# print(featmap_size) [200, 304]

# for i in range(5):
# print(seg_preds_x[i].shape)
# print(cate_preds[i].shape)
# torch.Size([1, 40, 200, 304])
# torch.Size([1, 40, 40, 80])
# torch.Size([1, 36, 200, 304])
# torch.Size([1, 36, 36, 80])
# torch.Size([1, 24, 200, 304])
# torch.Size([1, 24, 24, 80])
# torch.Size([1, 16, 200, 304])
# torch.Size([1, 16, 16, 80])
# torch.Size([1, 12, 200, 304])
# torch.Size([1, 12, 12, 80])

result_list = []
#由于是demo,这里只有一张img
for img_id in range(len(img_metas)):
cate_pred_list = [
cate_preds[i][img_id].view(-1, self.cate_out_channels).detach() for i in range(num_levels)
]
# print(cate_pred_list[0].shape) (num_grid*num_grid, 80)
seg_pred_list_x = [
seg_preds_x[i][img_id].detach() for i in range(num_levels)
]
# print(seg_pred_list_x[0].shape) #(num_grid, 200, 304)
seg_pred_list_y = [
seg_preds_y[i][img_id].detach() for i in range(num_levels)
]
img_shape = img_metas[img_id][‘img_shape’]
scale_factor = img_metas[img_id][‘scale_factor’]
ori_shape = img_metas[img_id][‘ori_shape’]

cate_pred_list = torch.cat(cate_pred_list, dim=0) #(3872, 80) == (40^2+36^2+24^2+16^2+12^2, 80)
seg_pred_list_x = torch.cat(seg_pred_list_x, dim=0) #(128, 200, 304) == (40+36+24+16+12, 200, 304)
# print(seg_pred_list_x.shapes)
seg_pred_list_y = torch.cat(seg_pred_list_y, dim=0)

result = self.get_seg_single(cate_pred_list, seg_pred_list_x, seg_pred_list_y,
featmap_size, img_shape, ori_shape, scale_factor, cfg, rescale)
result_list.append(result)
return result_list

def get_seg_single(self,
cate_preds,
seg_preds_x,
seg_preds_y,
featmap_size,
img_shape,
ori_shape,
scale_factor,
cfg,
rescale=False, debug=False):

# overall info.
h, w, _ = img_shape
upsampled_size_out = (featmap_size[0] * 4, featmap_size[1] * 4) # 原图大小

# trans trans_diff.
trans_size = torch.Tensor(self.seg_num_grids).pow(2).cumsum(0).long() # [1600, 2896, 3472, 3728, 3872]
trans_diff = torch.ones(trans_size[-1].item(), device=cate_preds.device).long()
num_grids = torch.ones(trans_size[-1].item(), device=cate_preds.device).long()
seg_size = torch.Tensor(self.seg_num_grids).cumsum(0).long()
seg_diff = torch.ones(trans_size[-1].item(), device=cate_preds.device).long()
strides = torch.ones(trans_size[-1].item(), device=cate_preds.device) # [1, 1, …, 1]

n_stage = len(self.seg_num_grids)
trans_diff[:trans_size[0]] *= 0
seg_diff[:trans_size[0]] *= 0
num_grids[:trans_size[0]] *= self.seg_num_grids[0]
# print(self.strides) [8, 8, 16, 32, 32]
strides[:trans_size[0]] *= self.strides[0]

for ind_ in range(1, n_stage):
trans_diff[trans_size[ind_ – 1]:trans_size[ind_]] *= trans_size[ind_ – 1]
seg_diff[trans_size[ind_ – 1]:trans_size[ind_]] *= seg_size[ind_ – 1]
num_grids[trans_size[ind_ – 1]:trans_size[ind_]] *= self.seg_num_grids[ind_]
strides[trans_size[ind_ – 1]:trans_size[ind_]] *= self.strides[ind_] # [0-1599:8, 1600-2895:8, 2896-3471: 16, 2372-3871:32]

# process.
inds = (cate_preds > cfg.score_thr)
# print(inds.shape) # [3872, 80]布尔矩阵
cate_scores = cate_preds[inds]
# print(cate_scores) # [3872, 80]

inds = inds.nonzero()
# print(inds.shape) # (n, 2) n表示有多少个分数>thres
trans_diff = torch.index_select(trans_diff, dim=0, index=inds[:, 0])
seg_diff = torch.index_select(seg_diff, dim=0, index=inds[:, 0])
num_grids = torch.index_select(num_grids, dim=0, index=inds[:, 0])
strides = torch.index_select(strides, dim=0, index=inds[:, 0])

y_inds = (inds[:, 0] – trans_diff) // num_grids
x_inds = (inds[:, 0] – trans_diff) % num_grids
y_inds += seg_diff
x_inds += seg_diff

cate_labels = inds[:, 1]
# print(cate_labels) # n维向量,表示类别num
seg_masks_soft = seg_preds_x[x_inds, …] * seg_preds_y[y_inds, …] # [n, 200, 304]
seg_masks = seg_masks_soft > cfg.mask_thr
sum_masks = seg_masks.sum((1, 2)).float() # [n, 1]
keep = sum_masks > strides # 进一步筛除,总的mask之和小于stride就筛掉
# print(keep)

seg_masks_soft = seg_masks_soft[keep, …]
seg_masks = seg_masks[keep, …]
cate_scores = cate_scores[keep]
sum_masks = sum_masks[keep]
cate_labels = cate_labels[keep]
# maskness
seg_score = (seg_masks_soft * seg_masks.float()).sum((1, 2)) / sum_masks
cate_scores *= seg_score

if len(cate_scores) == 0:
return None

# sort and keep top nms_pre
sort_inds = torch.argsort(cate_scores, descending=True)
if len(sort_inds) > cfg.nms_pre:
sort_inds = sort_inds[:cfg.nms_pre]
seg_masks_soft = seg_masks_soft[sort_inds, :, :]
seg_masks = seg_masks[sort_inds, :, :]
cate_scores = cate_scores[sort_inds]
sum_masks = sum_masks[sort_inds]
cate_labels = cate_labels[sort_inds]
# print(cate_scores)

# Matrix NMS
cate_scores = matrix_nms(seg_masks, cate_labels, cate_scores,
kernel=cfg.kernel, sigma=cfg.sigma, sum_masks=sum_masks)
# print(cate_scores) #维度并没变,只是将IOU高的部分的score降低,类似于soft-NMS

keep = cate_scores >= cfg.update_thr
seg_masks_soft = seg_masks_soft[keep, :, :]
cate_scores = cate_scores[keep]
# print(cate_scores.shape) #筛掉一部分
cate_labels = cate_labels[keep]
# sort and keep top_k
sort_inds = torch.argsort(cate_scores, descending=True)
if len(sort_inds) > cfg.max_per_img: # coco数据集*大一张img100个instance
sort_inds = sort_inds[:cfg.max_per_img]
seg_masks_soft = seg_masks_soft[sort_inds, :, :]
cate_scores = cate_scores[sort_inds]
cate_labels = cate_labels[sort_inds]

# 将mask的resolution还原到original图像大小
seg_masks_soft = F.interpolate(seg_masks_soft.unsqueeze(0),
size=upsampled_size_out,
mode=’bilinear’)[:, :, :h, :w]
seg_masks = F.interpolate(seg_masks_soft,
size=ori_shape[:2],
mode=’bilinear’).squeeze(0)
seg_masks = seg_masks > cfg.mask_thr

return seg_masks, cate_labels, cate_scores
*后在demo中在Matrix NMS之后,选择的筛除阈值为0.05,这个值有点小导致很多有小目标的img筛出来100个,*后demo在展示结果的时候又采用了0.25的阈值,这里会不会有些矛盾。

 

如何系统学习数据库?

如何系统学习数据库?
1 naoh1000 · 90 天前 via iPhone · 3026 次点击
这是一个创建于 90 天前的主题,其中的信息可能已经有所发展或是发生改变。
原本以为数据表设计能用就行了,自己个人项目的服务器 CPU 天天被数据库占满。找人优化了一下,CPU 占用只剩不到原来的 10%。
CPU 数据库 占用 服务器23 条回复 • 2021-02-18 18:23:50 +08:00
yfwl 1
yfwl 90 天前
一般来说数据库不需要优化,除非你是很大很大很大很大很大很大
MeatIndustry 2
MeatIndustry 90 天前 via iPhone
同求
lewis89 3
lewis89 90 天前
动手写一个 parse 照 innodb 写一个 b+树 按页 为*小单位实现
arthas2234 4
arthas2234 90 天前
写好 SQL,利用好索引,对一般人来说已经够用。剩下的是对性能有*致追求的
Procumbens 5
Procumbens 90 天前 ❤️ 2
书:Database System Concepts
课程:CMU 15-445
Lemeng 6
Lemeng 90 天前
等会再来看看大神集思广益
fiveelementgid 7
fiveelementgid 90 天前 via Android
ヘ(。□°)ヘ没学过数据库,Linq 中毒患者路过,等楼下大佬给方案
raaaaaar 8
raaaaaar 90 天前 via Android
买一本教科书然后开始刷
RickyC 9
RickyC 90 天前
@yfwl 数据库很大不是很正常?
jones2000 10
jones2000 89 天前
看数据库日志, 把慢的 sql 日志都打印出来 优化。

xupefei 11
xupefei 89 天前
来我司实习三个月
dream4ever 12
dream4ever 89 天前 ❤️ 1
用的是 MySQL 的话,我上豆瓣搜了搜,有几本中文的图书平分都在 8 分以上,包括《 MySQL 是怎样运行的》《 MySQL 必知必会》《高性能 MySQL 》,可以看看里面适合于入门的,比如前两本,应该会有帮助。
laminux29 13
laminux29 89 天前 ❤️ 2
既然你提到了 [系统性地学习] ,首先为你介绍一下计算机技能树:

模电 -> 数电 -> 组成 -> 汇编 -> C/C++ -> Java/C#/PHP/Python -> 操作系统 / 计算机网络 -> 传统数据库 -> 分布式 -> 分布式数据库

发现没,就算只解锁传统数据库,也需要学一大堆东西。

举个例子,12306 *版的数据库存在性能问题,在这条技能树上,向下就能穿透到模电层,后来他们*终采用的方案,本质上是解决了模电层的一个关键瓶颈。IBM 当时给的方案其实也是同类方法,只是不符合当时去 IOE 的政策,因此没被采用。
ttyhtg 14
ttyhtg 89 天前
我也想学呢,啥也不懂,网站总是资源消耗太多被停掉,问了说是数据库的问题,搜了教程照着优化了下,容量下去了,从占用 80%到了 16%,以为万事大吉了,结果今儿站点又被停了
crclz 15
crclz 89 天前
大学里面讲的“数据库概论”沾点关系代数了,并且和实际中如何写高效的 sql 语句重合性不高。

推荐书目:
《 SQL 反模式》(整本)
《高性能 MySQL 》(不必整本读)
liuxey 16
liuxey 89 天前
“找人优化”=”建了条索引”

其实日常设计和使用数据库也没啥高大上的东西,坑踩多了基本就熟练于心了
helloworld000 17
helloworld000 89 天前
跟着*专业的 CMU 的 database system 学
https://15445.courses.cs.cmu.edu/fall2020/

2020 年的还没出完,视频可以参考 2019 年的

Andy 是个非常有性格而且讲究实用性*重要的是有实力的大佬,比一些乱七八糟的阿猫阿狗和自己胡乱学好很多
fox0001 18
fox0001 89 天前
@liuxey #16 同意~查询速度的优化,一般用索引解决
helloworld000 19
helloworld000 89 天前 ❤️ 1
个人感觉 cmu 这个 database system 在四大里算是*好的 db 课程

紧贴业界*火的 db 产品和技术,也涉及了很多硬核的知识点,对整个 db 的知识框架梳理的非常清楚
lekai63 20
lekai63 89 天前 via iPhone
我觉得这需求 是不是看 Postgres 的文档 比较好?
huayumo 21
huayumo 89 天前
感觉数据库优化,索引占很大一部分
chihiro2014 22
chihiro2014 89 天前 via iPhone
@huayumo 查询优化器也是重点
zyf199601 23
zyf199601 42 天前
把 sql 写写好,数据表结构优化好

安卓不询问用户,默认授予位置权限

刚打开一个很久没用过的软件,弹了个什么权限,懒得看就点了拒*,然后看到状态栏有定位提示。设置里发现自动授予了位置权限,
清除数据(权限会被重置)又试了几次,*次打开时只会问管理通话和访问文件权限(都拒*了),然后再打开设置已经自动允许定位了……
正常设计不是未显式同意一律拒*吗,谷歌这样搞图个啥?有办法更改默认权限吗?安卓 10

27 条回复 • 2021-04-02 15:30:51 +08:00
qq73666 1
qq73666 5 小时 47 分钟前 ❤️ 3
几乎不可能默认授权,如果默认授权何必询问你要授权??*大可能是你点错了,无知不是信口开河的理由
S179276SP 2
S179276SP 5 小时 46 分钟前 via Android
什么手机?
alfchin 3
alfchin 5 小时 44 分钟前 via Android
MIUI+迪士尼救国?
LaTero 4
LaTero 5 小时 43 分钟前 via Android
@qq73666 刚又试了两次,*对没有点错,而且你没看清正文吧,它根本没问我要过位置的授权,只要过电话和储存。
@S179276SP 一加 7pro,LineageOS
app 是闲鱼,一年没用,看到卖 2080super 的帖子就上去看了一下
rosu 5
rosu 5 小时 38 分钟前 via Android
位置是危险权限。只可能是你的使用版本的 LOS 有 bug 。你可以直接试试其他软件就知道了。
littiefish 6
littiefish 5 小时 35 分钟前 via iPhone
哪个软件啊,说出来让大家避坑
xmumiffy 7
xmumiffy 5 小时 35 分钟前 via Android
应用 target 23- 就没动态权限,安装时默认给所有权限
LaTero 8
LaTero 5 小时 21 分钟前 via Android
@rosu 试了 alipay,绿色聊天软件 play 版( vx,不让发),但是它们都不会一启动就要位置权限,不确定到底是不是 bug,放假我更新一下系统吧。

@xmumiffy 但是它会问我电话和储存的权限……
HongJay 9
HongJay 5 小时 2 分钟前
软件越过系统吗。。
nashxk 10
nashxk 4 小时 44 分钟前
刚刚下载试了一下拒*后不会获得定位权限,包里写的 targetSdkVersion 是 28 。app 没更新?或者是 os 有问题?我的是 pixel3a,Android11

stephenxiaxy 11
stephenxiaxy 4 小时 38 分钟前
我玩 flutter 的时候,有两种方式,一种是代码里询问,另一种是直接写配置文件,第二种安装的时候是不需要询问的,默认已经有了
LaTero 12
LaTero 4 小时 21 分钟前 via Android
@nashxk 并没有拒*,因为它根本没有问…拒*的是电话和储存,位置没有问直接默认允许了。
dingwen07 13
dingwen07 1 小时 31 分钟前 via iPhone
把软件的目标 API 版本发出来
acrisliu 14
acrisliu 1 小时 29 分钟前 via Android
卸载重装试试,我的 Oxygen OS 清除数据不会重置权限。
AoEiuV020 15
AoEiuV020 1 小时 27 分钟前
感觉只能是 targetSdkVersion 太低,
zhangjiafan 16
zhangjiafan 1 小时 14 分钟前
targetSdkVersion 太低
LaTero 17
LaTero 58 分钟前 via Android
@dingwen07
@AoEiuV020
@zhangjiafan api 版本 26,试了另一个 api 版本 25 的地图程序也不会这样。而且 api 版本低就能偷权限不是可以被恶意利用吗
AoEiuV020 18
AoEiuV020 43 分钟前
@LaTero 建议把 apk 发出来大伙学习学习,
AoEiuV020 19
AoEiuV020 40 分钟前
@LaTero api 版本低就能偷权限这是没办法的,targetSdkVersion 低的典型情况是 app 开发时新版本的安卓根本没开发出来,人家当然没法遵守新版本的安全新机制,只是反过来让新版本安卓系统去兼容这些老 app,
而且也没想的那么严重,当年权限都是安装时展示出来安装等于授权,也用了很多年了,安全性不够高但也不是太值得担忧,
AoEiuV020 20
AoEiuV020 36 分钟前
@AoEiuV020 看到上面说的闲鱼了,我 miui12 刚试了下正常,有普通的弹出权限请求,
AoEiuV020 21
AoEiuV020 35 分钟前
还是建议把 apk 发出来看看,感觉用的不是一个东西,我试了下闲鱼*次打开就请求了定位,没有请求楼主说的“*次打开时只会问管理通话和访问文件权限”,
toptyloo 22
toptyloo 32 分钟前 via Android
@LaTero api 版本低商店会不让上架,然后机器能不能装上也不一定。
LaTero 23
LaTero 29 分钟前 via Android
@AoEiuV020 就是闲鱼,版本好像是 6.8.x,很久没更新了,今天看到个卖 2080s 的帖才打开看了下,现在更新到了 api28,会问位置权限了。
安卓 10 位置要手动开其实也不算特别不安全吧,只是我试了几个 lollipop 和 nougat 的程序都不会这样,感觉挺奇怪
LaTero 24
LaTero 26 分钟前 via Android
@AoEiuV020 打字慢了点,我没度盘账号,不知道咋发,版本 6.4.10
AoEiuV020 25
AoEiuV020 22 分钟前
@LaTero 临时文件我一般传到免注册的网站上,比如奶牛,
https://cowtransfer.com/
LaTero 26
LaTero 20 分钟前 via Android
@AoEiuV020 找了个国外共享盘:aHR0cHM6Ly91ZmlsZS5pby96YWswZnRmZA==(不让我发链接),又卸载重装了一次完美复现,安卓 10,安全补丁 2020 年 9 月 5 日
AoEiuV020 27
AoEiuV020 7 分钟前
@LaTero 包没发现什么特别的,只能怀疑是你的 los 有什么问题了,指不定是 rom 作者加了什么私料,我试了 miui 安卓 10 和魔趣安卓 9 都是正常申请权限。

win7 比win10还要快?

恰巧实验室有一台废弃的 win7 笔记本拿来用了用。*令我印象深刻的是打开资源管理器的速度,秒开!
再看看现在的 win10,双击起码要一秒才能打开,有时候从程序调用要等四五秒,真的拉跨!

63 条回复 • 2021-04-02 15:13:59 +08:00
Cooky 1
Cooky 22 小时 50 分钟前 via Android
win10 就是负优化,win7 内置的垃圾少
czfy 2
czfy 22 小时 49 分钟前
这边建议直接使用 win 95 呢,速度*快
x86 3
x86 22 小时 46 分钟前
我的 X1C 4th 都几乎秒开
ch2 4
ch2 22 小时 45 分钟前 via iPhone
*新的系统+*新的硬件
zhangfeiwudi 5
zhangfeiwudi 22 小时 44 分钟前
可以直接命令行 Ubuntu ps aux 也是秒开
mq4079 6
mq4079 22 小时 43 分钟前
dos 更快 快去用吧
love 7
love 22 小时 41 分钟前 via Android
建议转 linux,基本的资源管理器,命令行都是秒开,windows 开个命令行都要 3 秒
Tumblr 8
Tumblr 22 小时 36 分钟前
程序调用要 4 5 秒的建议检查自己的电脑或代码呢。。。
刚刚测试了一下用 PowerShell 打开资源管理器,用时 0.074 秒,具体点就是 74.8203ms 。
Tumblr 9
Tumblr 22 小时 33 分钟前
@Tumblr #8 又测了 10 次,数据如下,单位 ms:
24.3305
16.7634
16.8996
18.3595
25.0829
25.8236
20.1695
26.59
21.3152
17.538
BernieDu 10
BernieDu 22 小时 32 分钟前
windows10 打开资源管理器 1 秒吗。盲猜楼主奔腾 4

wowbaby 11
wowbaby 22 小时 30 分钟前
window10 各种联网上报有点恶习
est 12
est 22 小时 29 分钟前
winxp 更快。
zoewendel 13
zoewendel 22 小时 26 分钟前 via iPhone
我也不喜欢 win10
liuser666 14
liuser666 22 小时 26 分钟前
@BernieDu amd 2600,4800U 也是这样。
liuser666 15
liuser666 22 小时 23 分钟前
@Tumblr 怎么测的…我来试试
idhrwb01296 16
idhrwb01296 22 小时 21 分钟前
安迪比尔定律
liuser666 17
liuser666 22 小时 19 分钟前
@Tumblr 知道了,我 Measure-Command 测的 30,但是实际体验并不是这样,界面出来到完全可点击需要一秒的时间,而且如果你是很长时间没开资源管理器突然去打开它,还要等更久。
BernieDu 18
BernieDu 22 小时 19 分钟前
巧了我是 2600 并没有 1 秒
nannanziyu 19
nannanziyu 22 小时 15 分钟前
@liuser666
1..100 | select { Measure-Command {echo “exit 0” | powershell} }

测试 100 次
photon006 20
photon006 22 小时 13 分钟前
这个 cpu 有很大关系,我的主力笔记本用了好几年的三星 900x3c,cpu I5 3317U 打开各种窗口都比较慢,台式机 I5 10600KF 瞬间开。
bequt 21
bequt 22 小时 12 分钟前
要搞新硬件了
nannanziyu 22
nannanziyu 22 小时 12 分钟前
@liuser666
因为你测试 powershell -c 或者其他命令并没有模拟到打开界面时 powershell 连接到标准输入的过程
所以要用 echo “exit 0” | powershell 来模拟打开终端并且退出的过程
wangkun025 23
wangkun025 22 小时 10 分钟前
我也不喜欢 Windows10
Joshua999 24
Joshua999 21 小时 36 分钟前 via Android
有没有比较纯净的 windows 7 镜像,求推荐
Lemeng 25
Lemeng 21 小时 33 分钟前
硬件支持的话,Win7 确实是不错的系统
ho121 26
ho121 21 小时 31 分钟前 via Android
新电脑自从从 win10 换到 linux 之后,感觉好久没体验过这么流畅的操作了
nguoidiqua 27
nguoidiqua 21 小时 19 分钟前 ❤️ 1
@Joshua999

msdn.itellyou.cn
kokutou 28
kokutou 21 小时 13 分钟前 via Android
乱七八糟的卸载掉。。。
explorer 总有各种 dll 插进去,不慢才怪
kokutou 29
kokutou 21 小时 13 分钟前 via Android
explorer 正常是双击立刻就能打开的。
imn1 30
imn1 21 小时 6 分钟前
win7 是 OS,win10 是”personal server”,本质上不同
如果你把 win10 各种后台服务在 win7 加载并保持运行,估计也是惨*人寰
反向思维,win10 优化就要把不必要、不需要的服务关闭,只是很多都无法判断是否“需要”,&很多无法关闭
hlx 31
hlx 21 小时 6 分钟前 ❤️ 1
@Joshua999 https://next.itellyou.cn/Original/Index#
这个网站试一下
0bit 32
0bit 20 小时 49 分钟前
macOS Big Sur,16 款 MBP,打开 Finder,差不多也一秒,难受。
mmdsun 33
mmdsun 20 小时 43 分钟前 via Android
*版 Win10 速度*快。有个版本升级上去了速度立马慢了。

真的怀疑微软把 Win10 机械磁盘优化的代码给合并掉了。
jmk92 34
jmk92 20 小时 43 分钟前
我用虚拟机装了原生 win7,是真的流畅,用同样的配置安装的 win10 却略卡。
win10 自带的杀毒占 cpu,但也一定程度杜*了各种广告软件。
l4ever 35
l4ever 20 小时 36 分钟前
楼主搞个 ssd 看看
leven87 36
leven87 20 小时 31 分钟前
I use Win7 all the time, I don’t know why Microsoft update it to Win10, which seems not much optimization. My only concern about Win7 is Microsoft no longer provide Vulnerability patch:(
shijingshijing 37
shijingshijing 20 小时 25 分钟前 ❤️ 1
卸载掉人脉、地图等无卵用的内置模块,关掉各种 telemetry 服务,禁用各种日志写入,win 10 运行速度会有较大提升。
threebr 38
threebr 20 小时 22 分钟前
win10 有没有 ssd 是两个系统
slack 39
slack 20 小时 20 分钟前 via Android
如果是 intel CPU 的话可能没有打 Spectre 和 Meltdown
n2l 40
n2l 20 小时 14 分钟前 via iPhone
mba2014+win7 *配
Lightbright 41
Lightbright 20 小时 11 分钟前 via Android
优化得当可以比 win7 快,毕竟能更好地支持新硬件
abccccabc 42
abccccabc 19 小时 27 分钟前
@Joshua999 不要找纯净版了,除非你的硬件驱动都能自个找全。

我的笔记本由于预装 win10 家庭版,现在已经找不到 win7 的部分驱动,有人说官方就不再支持 win7 。而且还只能用国产的一键装机的镜像。
zictos 43
zictos 19 小时 1 分钟前
ssd 和 cpu 性能越好就越快
cuisflower 44
cuisflower 18 小时 57 分钟前
坦白讲 win95 一路用过来*喜欢的是 win10
leeg810312 45
leeg810312 17 小时 45 分钟前 via Android
看上去是 lz 测试问题。手头笔记本和台式机打开资源管理器都很快,无论什么打开方式,几年前的机器。win10 很早就开始用了,速度一向没有问题
lirunext 46
lirunext 17 小时 18 分钟前
bug10 太臃肿了,另外 win 就是这个德性,像安卓一样用久了容易卡。可以换 ltsc 之类的精简版系统,会好一点
Bestda 47
Bestda 16 小时 26 分钟前 via iPhone
ios6789 还很快,但慢慢的还是被淘汰
Sekai 48
Sekai 16 小时 13 分钟前
win10 我一辈子都不会用的
v2coolman 49
v2coolman 14 小时 0 分钟前
bug10 确实臃肿又拉垮,搞不懂怎么还有粉丝夸丝滑好用
easylee 50
easylee 13 小时 41 分钟前
debian 、macOs 秒开,happy?
fueen 51
fueen 6 小时 25 分钟前 ❤️ 1
经典 win7 踩 win10,真的太经典了
cxe2v 52
cxe2v 5 小时 36 分钟前
可以大胆猜测楼里挺 win7 踩 win10 的跟当年挺 xp 踩 win7 的人是一脉相承
godblessumilk 53
godblessumilk 5 小时 34 分钟前 via Android
CUI 和 GUI 的区别。命令行永远比图形界面快的啦?
liuser666 54
liuser666 3 小时 42 分钟前
@cxe2v 倒不是…我都没用过多长时间的 win7,只是拿到那台旧笔记本对打开速度表达震惊。
hafuhafu 55
hafuhafu 3 小时 38 分钟前
没啥感觉,硬件配置问题吧,10 可能需要的资源更多,不过硬件问题都不算问题。
ftu 56
ftu 3 小时 32 分钟前
打开 Office 比 M1 都快
stark123 57
stark123 2 小时 42 分钟前
win10 就是垃圾
RobinHu 58
RobinHu 2 小时 5 分钟前 via Android
我就觉得 win 很好用
RobinHu 59
RobinHu 2 小时 5 分钟前 via Android
win10
qooweds 60
qooweds 1 小时 16 分钟前
冲着 WSL 升的 win10,发现内存真的占用我承受不起
还是 win7 香
q197 61
q197 1 小时 16 分钟前
我觉得这个现象和快速访问收藏的文件夹数量,有没有挂载网络地址为硬盘有关系。例如有个经典 bug,拖动文件经过断开的挂载网络地址图标,整个 explorer 要卡好久。不过这些现象 win7 也有
hatw 62
hatw 1 小时 8 分钟前
我家里 10 年的台式电脑也没这么慢,当然用的 ssd
azoon 63
azoon 1 分钟前
思路刁钻~

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速